Library to control a Graphics TFT connected to 4-wire SPI - revised for the Raio RA8875 Display Controller.

Dependents:   FRDM_RA8875_mPaint RA8875_Demo RA8875_KeyPadDemo SignalGenerator ... more

Fork of SPI_TFT by Peter Drescher

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers RA8875.cpp Source File

RA8875.cpp

00001 // RA8875 Display Controller Library.
00002 //
00003 // This is being created for a Raio RA8875-based display from buydisplay.com,
00004 // which is either 480 x 272 or 800 x 480, using a 4-wire SPI interface.
00005 // Support is provided for both a keypad and a resistive touch-screen.
00006 //
00007 // See the RA8875.h file for full details.
00008 //
00009 // 20161106: Updated the initialization to set the various registers based on
00010 //           the BuyDisplay.com example code. This altered several registers
00011 //           for the 800x480 display driver.
00012 //
00013 #include "RA8875.h"
00014 
00015 //#include "Utility.h"            // private memory manager
00016 #ifndef UTILITY_H
00017 #define swMalloc malloc         // use the standard
00018 #define swFree free
00019 #endif
00020 
00021 //#define DEBUG "RAIO"
00022 // ...
00023 // INFO("Stuff to show %d", var); // new-line is automatically appended
00024 //
00025 #if (defined(DEBUG) && !defined(TARGET_LPC11U24))
00026 #define INFO(x, ...) std::printf("[INF %s %4d] " x "\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
00027 #define WARN(x, ...) std::printf("[WRN %s %4d] " x "\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
00028 #define ERR(x, ...)  std::printf("[ERR %s %4d] " x "\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
00029 static void HexDump(const char * title, const uint8_t * p, int count)
00030 {
00031     int i;
00032     char buf[100] = "0000: ";
00033 
00034     if (*title)
00035         INFO("%s", title);
00036     for (i=0; i<count; ) {
00037         sprintf(buf + strlen(buf), "%02X ", *(p+i));
00038         if ((++i & 0x0F) == 0x00) {
00039             INFO("%s", buf);
00040             if (i < count)
00041                 sprintf(buf, "%04X: ", i);
00042             else
00043                 buf[0] = '\0';
00044         }
00045     }
00046     if (strlen(buf))
00047         INFO("%s", buf);
00048 }
00049 #else
00050 #define INFO(x, ...)
00051 #define WARN(x, ...)
00052 #define ERR(x, ...)
00053 #define HexDump(a, b, c)
00054 #endif
00055 
00056 // Defaults. Users can override this with the init() method.
00057 #define RA8875_DISPLAY_WIDTH  480
00058 #define RA8875_DISPLAY_HEIGHT 272
00059 #define RA8875_COLORDEPTH_BPP 16    /* Not an API */
00060 
00061 #ifdef PERF_METRICS
00062 #define PERFORMANCE_RESET performance.reset()
00063 #define REGISTERPERFORMANCE(a) RegisterPerformance(a)
00064 #define COUNTIDLETIME(a) CountIdleTime(a)
00065 static const char *metricsName[] = {
00066     "Cls", "Pixel", "Pixel Stream", "Boolean Stream",
00067     "Read Pixel", "Read Pixel Stream",
00068     "Line",
00069     "Rectangle", "Rounded Rectangle",
00070     "Triangle", "Circle", "Ellipse"
00071 };
00072 uint16_t commandsUsed[256];  // track which commands are used with simple counter of number of hits.
00073 #else
00074 #define PERFORMANCE_RESET
00075 #define REGISTERPERFORMANCE(a)
00076 #define COUNTIDLETIME(a)
00077 #endif
00078 
00079 // When it is going to poll a register for completion, how many
00080 // uSec should it wait between each polling activity.
00081 #define POLLWAITuSec 10
00082 
00083 // Private RawKeyMap for the Keyboard interface
00084 static const uint8_t DefaultKeyMap[22] = {
00085     0,
00086     1,  2,  3,  4,  5,  6,  7,  8,  9, 10,
00087     11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
00088     255
00089 };
00090 
00091 static const char * ErrMessages[] = {
00092     "noerror",                ///< no errors, command completed successfully
00093     "bad parameter",          ///< one or more parameters are invalid
00094     "file not found",         ///< specified file could not be found
00095     "not bmp format",         ///< file is not a .bmp file
00096     "not ico format",         ///< file is not a .ico file
00097     "not supported format",   ///< file format is not yet supported
00098     "image too big",          ///< image is too large for the screen
00099     "not enough ram",         ///< could not allocate ram for scanline
00100     "touch cal. timeout",     ///< calibration could not complete in time
00101     "external abort",         ///< during an idle callback, the user code initiated an abort
00102 };
00103 
00104 typedef struct {
00105     uint8_t b;
00106     uint8_t g;
00107     uint8_t r;
00108     uint8_t a;
00109 } rgbTrio_t;
00110 
00111 /// This is defined as a "Web-Safe" color palette of 216 colors.
00112 ///
00113 /// It is defined so it can be emitted into a BMP file as the color palette, and it is then used
00114 /// for downscaling from higher resolution color depth to an 8-bit format.
00115 ///
00116 static const rgbTrio_t WebColorPalette[] = {
00117     {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x33,0xFF}, {0x00,0x00,0x66,0xFF}, {0x00,0x00,0x99,0xFF}, {0x00,0x00,0xCC,0xFF}, {0x00,0x00,0xFF,0xFF},
00118     {0x00,0x33,0x00,0xFF}, {0x00,0x33,0x33,0xFF}, {0x00,0x33,0x66,0xFF}, {0x00,0x33,0x99,0xFF}, {0x00,0x33,0xCC,0xFF}, {0x00,0x33,0xFF,0xFF},
00119     {0x00,0x66,0x00,0xFF}, {0x00,0x66,0x33,0xFF}, {0x00,0x66,0x66,0xFF}, {0x00,0x66,0x99,0xFF}, {0x00,0x66,0xCC,0xFF}, {0x00,0x66,0xFF,0xFF},
00120     {0x00,0x99,0x00,0xFF}, {0x00,0x99,0x33,0xFF}, {0x00,0x99,0x66,0xFF}, {0x00,0x99,0x99,0xFF}, {0x00,0x99,0xCC,0xFF}, {0x00,0x99,0xFF,0xFF},
00121     {0x00,0xCC,0x00,0xFF}, {0x00,0xCC,0x33,0xFF}, {0x00,0xCC,0x66,0xFF}, {0x00,0xCC,0x99,0xFF}, {0x00,0xCC,0xCC,0xFF}, {0x00,0xCC,0xFF,0xFF},
00122     {0x00,0xFF,0x00,0xFF}, {0x00,0xFF,0x33,0xFF}, {0x00,0xFF,0x66,0xFF}, {0x00,0xFF,0x99,0xFF}, {0x00,0xFF,0xCC,0xFF}, {0x00,0xFF,0xFF,0xFF},
00123     {0x33,0x00,0x00,0xFF}, {0x33,0x00,0x33,0xFF}, {0x33,0x00,0x66,0xFF}, {0x33,0x00,0x99,0xFF}, {0x33,0x00,0xCC,0xFF}, {0x33,0x00,0xFF,0xFF},
00124     {0x33,0x33,0x00,0xFF}, {0x33,0x33,0x33,0xFF}, {0x33,0x33,0x66,0xFF}, {0x33,0x33,0x99,0xFF}, {0x33,0x33,0xCC,0xFF}, {0x33,0x33,0xFF,0xFF},
00125     {0x33,0x66,0x00,0xFF}, {0x33,0x66,0x33,0xFF}, {0x33,0x66,0x66,0xFF}, {0x33,0x66,0x99,0xFF}, {0x33,0x66,0xCC,0xFF}, {0x33,0x66,0xFF,0xFF},
00126     {0x33,0x99,0x00,0xFF}, {0x33,0x99,0x33,0xFF}, {0x33,0x99,0x66,0xFF}, {0x33,0x99,0x99,0xFF}, {0x33,0x99,0xCC,0xFF}, {0x33,0x99,0xFF,0xFF},
00127     {0x33,0xCC,0x00,0xFF}, {0x33,0xCC,0x33,0xFF}, {0x33,0xCC,0x66,0xFF}, {0x33,0xCC,0x99,0xFF}, {0x33,0xCC,0xCC,0xFF}, {0x33,0xCC,0xFF,0xFF},
00128     {0x33,0xFF,0x00,0xFF}, {0x33,0xFF,0x33,0xFF}, {0x33,0xFF,0x66,0xFF}, {0x33,0xFF,0x99,0xFF}, {0x33,0xFF,0xCC,0xFF}, {0x33,0xFF,0xFF,0xFF},
00129     {0x66,0x00,0x00,0xFF}, {0x66,0x00,0x33,0xFF}, {0x66,0x00,0x66,0xFF}, {0x66,0x00,0x99,0xFF}, {0x66,0x00,0xCC,0xFF}, {0x66,0x00,0xFF,0xFF},
00130     {0x66,0x33,0x00,0xFF}, {0x66,0x33,0x33,0xFF}, {0x66,0x33,0x66,0xFF}, {0x66,0x33,0x99,0xFF}, {0x66,0x33,0xCC,0xFF}, {0x66,0x33,0xFF,0xFF},
00131     {0x66,0x66,0x00,0xFF}, {0x66,0x66,0x33,0xFF}, {0x66,0x66,0x66,0xFF}, {0x66,0x66,0x99,0xFF}, {0x66,0x66,0xCC,0xFF}, {0x66,0x66,0xFF,0xFF},
00132     {0x66,0x99,0x00,0xFF}, {0x66,0x99,0x33,0xFF}, {0x66,0x99,0x66,0xFF}, {0x66,0x99,0x99,0xFF}, {0x66,0x99,0xCC,0xFF}, {0x66,0x99,0xFF,0xFF},
00133     {0x66,0xCC,0x00,0xFF}, {0x66,0xCC,0x33,0xFF}, {0x66,0xCC,0x66,0xFF}, {0x66,0xCC,0x99,0xFF}, {0x66,0xCC,0xCC,0xFF}, {0x66,0xCC,0xFF,0xFF},
00134     {0x66,0xFF,0x00,0xFF}, {0x66,0xFF,0x33,0xFF}, {0x66,0xFF,0x66,0xFF}, {0x66,0xFF,0x99,0xFF}, {0x66,0xFF,0xCC,0xFF}, {0x66,0xFF,0xFF,0xFF},
00135     {0x99,0x00,0x00,0xFF}, {0x99,0x00,0x33,0xFF}, {0x99,0x00,0x66,0xFF}, {0x99,0x00,0x99,0xFF}, {0x99,0x00,0xCC,0xFF}, {0x99,0x00,0xFF,0xFF},
00136     {0x99,0x33,0x00,0xFF}, {0x99,0x33,0x33,0xFF}, {0x99,0x33,0x66,0xFF}, {0x99,0x33,0x99,0xFF}, {0x99,0x33,0xCC,0xFF}, {0x99,0x33,0xFF,0xFF},
00137     {0x99,0x66,0x00,0xFF}, {0x99,0x66,0x33,0xFF}, {0x99,0x66,0x66,0xFF}, {0x99,0x66,0x99,0xFF}, {0x99,0x66,0xCC,0xFF}, {0x99,0x66,0xFF,0xFF},
00138     {0x99,0x99,0x00,0xFF}, {0x99,0x99,0x33,0xFF}, {0x99,0x99,0x66,0xFF}, {0x99,0x99,0x99,0xFF}, {0x99,0x99,0xCC,0xFF}, {0x99,0x99,0xFF,0xFF},
00139     {0x99,0xCC,0x00,0xFF}, {0x99,0xCC,0x33,0xFF}, {0x99,0xCC,0x66,0xFF}, {0x99,0xCC,0x99,0xFF}, {0x99,0xCC,0xCC,0xFF}, {0x99,0xCC,0xFF,0xFF},
00140     {0x99,0xFF,0x00,0xFF}, {0x99,0xFF,0x33,0xFF}, {0x99,0xFF,0x66,0xFF}, {0x99,0xFF,0x99,0xFF}, {0x99,0xFF,0xCC,0xFF}, {0x99,0xFF,0xFF,0xFF},
00141     {0xCC,0x00,0x00,0xFF}, {0xCC,0x00,0x33,0xFF}, {0xCC,0x00,0x66,0xFF}, {0xCC,0x00,0x99,0xFF}, {0xCC,0x00,0xCC,0xFF}, {0xCC,0x00,0xFF,0xFF},
00142     {0xCC,0x33,0x00,0xFF}, {0xCC,0x33,0x33,0xFF}, {0xCC,0x33,0x66,0xFF}, {0xCC,0x33,0x99,0xFF}, {0xCC,0x33,0xCC,0xFF}, {0xCC,0x33,0xFF,0xFF},
00143     {0xCC,0x66,0x00,0xFF}, {0xCC,0x66,0x33,0xFF}, {0xCC,0x66,0x66,0xFF}, {0xCC,0x66,0x99,0xFF}, {0xCC,0x66,0xCC,0xFF}, {0xCC,0x66,0xFF,0xFF},
00144     {0xCC,0x99,0x00,0xFF}, {0xCC,0x99,0x33,0xFF}, {0xCC,0x99,0x66,0xFF}, {0xCC,0x99,0x99,0xFF}, {0xCC,0x99,0xCC,0xFF}, {0xCC,0x99,0xFF,0xFF},
00145     {0xCC,0xCC,0x00,0xFF}, {0xCC,0xCC,0x33,0xFF}, {0xCC,0xCC,0x66,0xFF}, {0xCC,0xCC,0x99,0xFF}, {0xCC,0xCC,0xCC,0xFF}, {0xCC,0xCC,0xFF,0xFF},
00146     {0xCC,0xFF,0x00,0xFF}, {0xCC,0xFF,0x33,0xFF}, {0xCC,0xFF,0x66,0xFF}, {0xCC,0xFF,0x99,0xFF}, {0xCC,0xFF,0xCC,0xFF}, {0xCC,0xFF,0xFF,0xFF},
00147     {0xFF,0x00,0x00,0xFF}, {0xFF,0x00,0x33,0xFF}, {0xFF,0x00,0x66,0xFF}, {0xFF,0x00,0x99,0xFF}, {0xFF,0x00,0xCC,0xFF}, {0xFF,0x00,0xFF,0xFF},
00148     {0xFF,0x33,0x00,0xFF}, {0xFF,0x33,0x33,0xFF}, {0xFF,0x33,0x66,0xFF}, {0xFF,0x33,0x99,0xFF}, {0xFF,0x33,0xCC,0xFF}, {0xFF,0x33,0xFF,0xFF},
00149     {0xFF,0x66,0x00,0xFF}, {0xFF,0x66,0x33,0xFF}, {0xFF,0x66,0x66,0xFF}, {0xFF,0x66,0x99,0xFF}, {0xFF,0x66,0xCC,0xFF}, {0xFF,0x66,0xFF,0xFF},
00150     {0xFF,0x99,0x00,0xFF}, {0xFF,0x99,0x33,0xFF}, {0xFF,0x99,0x66,0xFF}, {0xFF,0x99,0x99,0xFF}, {0xFF,0x99,0xCC,0xFF}, {0xFF,0x99,0xFF,0xFF},
00151     {0xFF,0xCC,0x00,0xFF}, {0xFF,0xCC,0x33,0xFF}, {0xFF,0xCC,0x66,0xFF}, {0xFF,0xCC,0x99,0xFF}, {0xFF,0xCC,0xCC,0xFF}, {0xFF,0xCC,0xFF,0xFF},
00152     {0xFF,0xFF,0x00,0xFF}, {0xFF,0xFF,0x33,0xFF}, {0xFF,0xFF,0x66,0xFF}, {0xFF,0xFF,0x99,0xFF}, {0xFF,0xFF,0xCC,0xFF}, {0xFF,0xFF,0xFF,0xFF},
00153 
00154     {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF},
00155     {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF},
00156     {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF},
00157     {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF},
00158     {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF},
00159     {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF},
00160     {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF},
00161 };
00162 
00163 #define sqr(a) ((a) * (a))
00164 
00165 /// Find the nearest color match in the lookup table.
00166 ///
00167 /// The typical process is to find the difference between a given color and each entry in
00168 /// the table. The difference is defined as:
00169 ///     diff = sqrt(sqr(r - table[i].r) + sqr(g - table[i].g) + sqr(b - table[i].b))
00170 /// The square root function is very CPU intensive, especially w/o a floating point unit,
00171 /// so that step is omitted to speed it up a bit.
00172 ///
00173 static int FindNearestWebColor(uint8_t r, uint8_t g, uint8_t b) {
00174     int bestNdx = 0;
00175     float bestDiff = (sqr(r - WebColorPalette[0].r) + sqr(g - WebColorPalette[0].g) + sqr(b - WebColorPalette[0].b));
00176     for (int i=1; i<216; i++) {
00177         float thisDiff = (sqr(r - WebColorPalette[i].r) + sqr(g - WebColorPalette[i].g) + sqr(b - WebColorPalette[i].b));
00178         if (thisDiff < bestDiff) {
00179             bestDiff = thisDiff;
00180             bestNdx = i;
00181         }
00182     }
00183     return bestNdx;
00184 }
00185 
00186 // Non-Touch, or Resistive Touch when later initialized that way
00187 //
00188 RA8875::RA8875(PinName mosi, PinName miso, PinName sclk, PinName csel, PinName reset,
00189     const char *name)
00190     : GraphicsDisplay(name)
00191     , spi(mosi, miso, sclk)
00192     , cs(csel)
00193     , res(NULL)
00194 {
00195     INFO("RA8875");
00196     if (reset != NC)
00197         res = new DigitalOut(reset);
00198     InitAllMemberVars();
00199     touchInfo = (touchInfo_T *)malloc(RESISTIVE_TOUCH_POINTS * sizeof(touchInfo_T));
00200     if (touchInfo)
00201         useTouchPanel = TP_RES;
00202 }
00203 
00204 // Touch, based on FT5206 Controller Chip
00205 //
00206 RA8875::RA8875(PinName mosi, PinName miso, PinName sclk, PinName csel, PinName reset,
00207     PinName sda, PinName scl, PinName irq, const char * name)
00208     : GraphicsDisplay(name)
00209     , spi(mosi, miso, sclk)
00210     , cs(csel)
00211     , res(NULL)
00212 {
00213     INFO("RA8875");
00214     if (reset != NC)
00215         res = new DigitalOut(reset);
00216     InitAllMemberVars();
00217     m_irq = new InterruptIn(irq);
00218     m_i2c = new I2C(sda, scl);
00219     INFO("m_i2c = %p", m_i2c);
00220 
00221     // Cap touch panel config
00222     touchInfo = (touchInfo_T *)malloc(FT5206_TOUCH_POINTS * sizeof(touchInfo_T));
00223     if (touchInfo)
00224         useTouchPanel = TP_FT5206;
00225     m_addr = (FT5206_I2C_ADDRESS << 1);
00226     m_i2c->frequency(FT5206_I2C_FREQUENCY);
00227 
00228     // Interrupt
00229     m_irq->mode(PullUp);
00230     #if MBED_VERSION >= MBED_ENCODE_VERSION(5,8,0)
00231     eventThread.start(callback(&queue, &EventQueue::dispatch_forever));
00232     m_irq->fall(queue.event(callback(this, &RA8875::TouchPanelISR)));
00233     #elif (MBED_MAJOR_VERSION >= 5) || (MBED_LIBRARY_VERSION > 127)
00234     m_irq->fall(callback(this, &RA8875::TouchPanelISR));
00235     #else
00236     m_irq->fall(this, &RA8875::TouchPanelISR);
00237     #endif
00238     m_irq->disable_irq();   // ensure it is disabled until the init()
00239     TouchPanelInit();
00240     INFO("RA8875 end.");
00241 }
00242 
00243 
00244 // Touch, based on GSL1680 Controller Chip
00245 //
00246 RA8875::RA8875(PinName mosi, PinName miso, PinName sclk, PinName csel, PinName reset,
00247     PinName sda, PinName scl, PinName wake, PinName irq, const char * name)
00248     : GraphicsDisplay(name)
00249     , spi(mosi, miso, sclk)
00250     , cs(csel)
00251     , res(NULL)
00252 {
00253     INFO("RA8875");
00254     if (reset != NC)
00255         res = new DigitalOut(reset);
00256     InitAllMemberVars();
00257 
00258     m_irq = new InterruptIn(irq);
00259     m_i2c = new I2C(sda, scl);
00260 
00261     // Cap touch panel config
00262     touchInfo = (touchInfo_T *)malloc(FT5206_TOUCH_POINTS * sizeof(touchInfo_T));
00263     if (touchInfo)
00264         useTouchPanel = TP_GSL1680;
00265     m_addr = (GSL1680_I2C_ADDRESS << 1);
00266     m_i2c->frequency(GSL1680_I2C_FREQUENCY);
00267     m_wake = new DigitalOut(wake);
00268 
00269     // Interrupt
00270     m_irq->mode(PullUp);
00271     #if MBED_VERSION >= MBED_ENCODE_VERSION(5,8,0)
00272     eventThread.start(callback(&queue, &EventQueue::dispatch_forever));
00273     m_irq->fall(queue.event(callback(this, &RA8875::TouchPanelISR)));
00274     #elif (MBED_MAJOR_VERSION >= 5) || (MBED_LIBRARY_VERSION > 127)
00275     m_irq->fall(callback(this, &RA8875::TouchPanelISR));
00276     #else
00277     m_irq->fall(this, &RA8875::TouchPanelISR);
00278     #endif
00279     m_irq->disable_irq();   // ensure it is disabled until the init()
00280     TouchPanelInit();
00281 }
00282 
00283 
00284 
00285 //RA8875::~RA8875()
00286 //{
00287 //}
00288 
00289 void RA8875::InitAllMemberVars() {
00290     tpFQFN = NULL;
00291     tpCalMessage = NULL;
00292     c_callback = NULL;
00293     obj_callback = NULL;
00294     method_callback = NULL;
00295     idle_callback = NULL;
00296     fontScaleX = fontScaleY = 1;
00297     m_addr = 0;
00298     m_wake = NULL;      // not used for FT5206
00299     m_irq = NULL;
00300     m_i2c = NULL;
00301     touchInfo = NULL;
00302     useTouchPanel = TP_NONE;
00303     touchState = no_touch;
00304     numberOfTouchPoints = 0;
00305     gesture = 0;
00306     panelTouched = false;
00307     touchSample = 0;
00308     memset(&tpMatrix, 0, sizeof(tpMatrix_t));
00309     pKeyMap = NULL;
00310     spiWriteSpeed = false;
00311     spiwritefreq = 1000000;
00312     spireadfreq = 1000000;
00313     screenbpp = 16;
00314     virt_screenwidth = 0;
00315     virt_screenheight = 0;
00316     memset(&windowrect, 0, sizeof(rect_t));
00317     font = NULL;
00318     extFontHeight = 0;
00319     extFontWidth = 0;
00320     cursor_x = 0;
00321     cursor_y = 0;
00322     _printFH = NULL;
00323     roundCap = false;
00324     screen_orientation = normal;
00325 #ifdef PERF_METRICS
00326     ClearPerformance();
00327 #endif
00328 }
00329 
00330 RetCode_t RA8875::init(int width, int height, int color_bpp, uint8_t poweron, bool keypadon, bool touchscreenon)
00331 {
00332     INFO("RA8875::init()");
00333     font = NULL;                                // no external font, use internal.
00334     pKeyMap = DefaultKeyMap;                    // set default key map
00335     _select(false);                             // deselect the display
00336     frequency(RA8875_DEFAULT_SPI_FREQ);         // data rate
00337     Reset();
00338     // Set PLL based on display size from buy-display.com sample code
00339     // RAIO Reference display values noted: 800w: 0x0B, 480w: 0x0A
00340     if (width == 800) {
00341         WriteCommand(RA8875_PLLC1, 0x0C);               // PLLC1 - Phase Lock Loop registers [RAIO 0B]
00342     } else {
00343         WriteCommand(RA8875_PLLC1, 0x0B);               // PLLC1 - Phase Lock Loop registers [RAIO 0A]
00344     }
00345     wait_us(1000);
00346     WriteCommand(RA8875_PLLC2, 0x02);
00347     wait_us(1000);
00348 
00349     // System Config Register (SYSR)
00350     screenbpp = color_bpp;
00351     if (color_bpp == 16) {
00352         WriteCommand(RA8875_SYSR, 0x0C);               // 16-bpp (65K colors) color depth, 8-bit interface
00353     } else { // color_bpp == 8
00354         WriteCommand(RA8875_SYSR, 0x00);               // 8-bpp (256 colors)
00355     }
00356 
00357     // Set Pixel Clock Setting Register (PCSR) based on display size from buy-display.com sample code
00358     if (width == 800) {
00359         WriteCommand(RA8875_PCSR, 0x81);               // PDAT on PCLK falling edge, PCLK = 4 x System Clock
00360         wait_us(1000);
00361 
00362         // Horizontal Settings
00363         virt_screenwidth = width;
00364         WriteCommand(RA8875_HDWR, width/8 - 1);            //HDWR//Horizontal Display Width Setting Bit[6:0]
00365         WriteCommand(RA8875_HNDFTR, 0x00);                 //HNDFCR//Horizontal Non-Display Period fine tune Bit[3:0]
00366         WriteCommand(RA8875_HNDR, 0x03);                   //HNDR//Horizontal Non-Display Period Bit[4:0] [RAIO 0x01]
00367         WriteCommand(RA8875_HSTR, 0x03);                   //HSTR//HSYNC Start Position[4:0] [RAIO 0x00]
00368         WriteCommand(RA8875_HPWR, 0x0B);                   //HPWR//HSYNC Polarity ,The period width of HSYNC. [RAIO 0x05]
00369 
00370         // Vertical Settings
00371         virt_screenheight = height;
00372         WriteCommand(RA8875_VDHR0, (height-1)&0xFF);        //VDHR0 //Vertical Display Height Bit [7:0]
00373         WriteCommand(RA8875_VDHR1, (height-1)>>8);          //VDHR1 //Vertical Display Height Bit [8]
00374         WriteCommand(RA8875_VNDR0, 0x20);                   //VNDR0 //Vertical Non-Display Period Bit [7:0] [RAIO 0x02]
00375         WriteCommand(RA8875_VNDR1, 0x00);                   //VNDR1 //Vertical Non-Display Period Bit [8]
00376         WriteCommand(RA8875_VSTR0, 0x16);                   //VSTR0 //VSYNC Start Position[7:0] [RAIO 0x07]
00377         WriteCommand(RA8875_VSTR1, 0x00);                   //VSTR1 //VSYNC Start Position[8]
00378         WriteCommand(RA8875_VPWR, 0x01);                   //VPWR  //VSYNC Polarity ,VSYNC Pulse Width[6:0] [RAIO 0x09]
00379     } else {
00380         WriteCommand(RA8875_PCSR, 0x82);               // PDAT on PCLK falling edge, PCLK = 4 x System Clock
00381         wait_us(1000);
00382 
00383         // Horizontal Settings
00384         virt_screenwidth = width;
00385         WriteCommand(RA8875_HDWR, width/8 - 1);            //HDWR//Horizontal Display Width Setting Bit[6:0]
00386         WriteCommand(RA8875_HNDFTR, 0x02);                   //HNDFCR//Horizontal Non-Display Period fine tune Bit[3:0]
00387         WriteCommand(RA8875_HNDR, 0x03);                   //HNDR//Horizontal Non-Display Period Bit[4:0]
00388         WriteCommand(RA8875_HSTR, 0x01);                   //HSTR//HSYNC Start Position[4:0]
00389         WriteCommand(RA8875_HPWR, 0x03);                   //HPWR//HSYNC Polarity ,The period width of HSYNC.
00390 
00391         // Vertical Settings
00392         virt_screenheight = height;
00393         WriteCommand(RA8875_VDHR0, (height-1)&0xFF);        //VDHR0 //Vertical Display Height Bit [7:0]
00394         WriteCommand(RA8875_VDHR1, (height-1)>>8);          //VDHR1 //Vertical Display Height Bit [8]
00395         WriteCommand(RA8875_VNDR0, 0x0F);                   //VNDR0 //Vertical Non-Display Period Bit [7:0]
00396         WriteCommand(RA8875_VNDR1, 0x00);                   //VNDR1 //Vertical Non-Display Period Bit [8]
00397         WriteCommand(RA8875_VSTR0, 0x0e);                   //VSTR0 //VSYNC Start Position[7:0]
00398         WriteCommand(RA8875_VSTR1, 0x06);                   //VSTR1 //VSYNC Start Position[8]
00399         WriteCommand(RA8875_VPWR, 0x01);                   //VPWR  //VSYNC Polarity ,VSYNC Pulse Width[6:0]
00400     }
00401 
00402     if (width >= 800 && height >= 480 && color_bpp > 8) {
00403         WriteCommand(RA8875_DPCR, 0x00);               // DPCR - 1-layer mode when the resolution is too high
00404     } else {
00405         WriteCommand(RA8875_DPCR, 0x80);               // DPCR - 2-layer mode
00406     }
00407 
00408     // Set display image to Blue on Black as default
00409     SetWindow(0,0, width-1, height-1);             // Initialize to full screen
00410     SetTextCursorControl();
00411     foreground(Blue);
00412     background(Black);
00413     cls(3);
00414 
00415     Power(poweron);
00416     Backlight_u8(poweron);
00417     if (keypadon)
00418         KeypadInit();
00419     if (touchscreenon) {
00420         if (useTouchPanel == TP_NONE)
00421             useTouchPanel = TP_RES;
00422         if (useTouchPanel == TP_RES)
00423             TouchPanelInit();       // only init again if resistive (due to HW reset applied above).
00424     }
00425 #ifdef PERF_METRICS
00426     performance.start();
00427     ClearPerformance();
00428 #endif
00429     INFO("RA8875::init() end");
00430     return noerror;
00431 }
00432 
00433 
00434 RetCode_t RA8875::Reset(void)
00435 {
00436     RetCode_t ret;
00437 
00438     #if 1
00439     if (res) {
00440         res->write(0);                      // Active low - assert reset
00441         wait_us(2000);                      // must be > 1024 clock periods. (@25 MHz, this is 40.96 usec)
00442         res->write(1);                      // de-assert reset
00443     }
00444     #endif
00445     ret = WriteCommand(RA8875_PWRR, 0x01);         // Apply Display Off, Reset
00446     wait_us(2000);                             // no idea if I need to wait, or how long
00447     if (ret == noerror) {
00448         ret = WriteCommand(RA8875_PWRR, 0x00);     // Display off, Remove reset
00449         wait_us(2000);                         // no idea if I need to wait, or how long
00450     }
00451     return ret;
00452 }
00453 
00454 
00455 const char * RA8875::GetErrorMessage(RetCode_t code)
00456 {
00457     if (code >= LastErrCode)
00458         code = bad_parameter;
00459     return ErrMessages[code];
00460 }
00461 
00462 
00463 uint16_t RA8875::GetDrawingLayer(void)
00464 {
00465     return (ReadCommand(0x41) & 0x01);
00466 }
00467 
00468 
00469 uint16_t RA8875::SelectDrawingLayer(uint16_t layer)
00470 {
00471     uint16_t prevLayer;
00472     unsigned char mwcr1 = ReadCommand(0x41); // retain all but the currently selected layer
00473     prevLayer = (mwcr1 & 1);
00474     mwcr1 &= ~0x01; // remove the current layer
00475     if (virt_screenwidth >= 800 && virt_screenheight >= 480 && screenbpp > 8) {
00476         layer = 0;
00477     } else if (layer > 1) {
00478         layer = 0;
00479     }
00480     WriteCommand(RA8875_MWCR1, mwcr1 | layer);
00481     return prevLayer;
00482 }
00483 
00484 
00485 RA8875::LayerMode_T RA8875::GetLayerMode(void)
00486 {
00487     return (LayerMode_T)(ReadCommand(0x52) & 0x7);
00488 }
00489 
00490 
00491 RA8875::LayerMode_T RA8875::SetLayerMode(LayerMode_T mode)
00492 {
00493     unsigned char ltpr0 = ReadCommand(0x52); // retain all but the display layer mode
00494     LayerMode_T oldLayer = (LayerMode_T)(ltpr0 & 0x07);
00495     if (mode <= (LayerMode_T)6) {
00496         WriteCommand(RA8875_LTPR0, (ltpr0 & ~0x07) | (mode & 0x07));
00497     }
00498     return oldLayer;
00499 }
00500 
00501 
00502 RetCode_t RA8875::SetLayerTransparency(uint8_t layer1, uint8_t layer2)
00503 {
00504     if (layer1 > 8)
00505         layer1 = 8;
00506     if (layer2 > 8)
00507         layer2 = 8;
00508     WriteCommand(RA8875_LTPR1, ((layer2 & 0xF) << 4) | (layer1 & 0xF));
00509     return noerror;
00510 }
00511 
00512 
00513 color_t RA8875::SetBackgroundTransparencyColor(color_t color)
00514 {
00515     color_t oldColor = _readColorTrio(0x67);
00516     _writeColorTrio(0x67, color);
00517     return oldColor;
00518 }
00519 
00520 
00521 color_t RA8875::GetBackgroundTransparencyColor(void)
00522 {
00523     RGBQUAD q;
00524 
00525     q.rgbRed = ReadCommand(0x67);
00526     q.rgbGreen = ReadCommand(0x68);
00527     q.rgbBlue = ReadCommand(0x69);
00528     return RGBQuadToRGB16(&q, 0);
00529 }
00530 
00531 
00532 RetCode_t RA8875::KeypadInit(bool scanEnable, bool longDetect, uint8_t sampleTime, uint8_t scanFrequency,
00533                              uint8_t longTimeAdjustment, bool interruptEnable, bool wakeupEnable)
00534 {
00535     uint8_t value = 0;
00536 
00537     if (sampleTime > 3 || scanFrequency > 7 || longTimeAdjustment  > 3)
00538         return bad_parameter;
00539     value |= (scanEnable) ? 0x80 : 0x00;
00540     value |= (longDetect) ? 0x40 : 0x00;
00541     value |= (sampleTime & 0x03) << 4;
00542     value |= (scanFrequency & 0x07);
00543     WriteCommand(RA8875_KSCR1, value);   // KSCR1 - Enable Key Scan (and ignore possibility of an error)
00544 
00545     value = 0;
00546     value |= (wakeupEnable) ? 0x80 : 0x00;
00547     value |= (longTimeAdjustment & 0x03) << 2;
00548     WriteCommand(RA8875_KSCR2, value);  // KSCR2 - (and ignore possibility of an error)
00549 
00550     value = ReadCommand(0xF0);          // (and ignore possibility of an error)
00551     value &= ~0x10;
00552     value |= (interruptEnable) ? 0x10 : 0x00;
00553     return WriteCommand(RA8875_INTC1, value);   // INT
00554 }
00555 
00556 
00557 RetCode_t RA8875::SetKeyMap(const uint8_t * CodeList)
00558 {
00559     pKeyMap = CodeList;
00560     return noerror;
00561 }
00562 
00563 
00564 bool RA8875::readable(void)
00565 {
00566     return (ReadCommand(0xF1) & 0x10);  // check KS status - true if kbhit
00567 }
00568 
00569 
00570 uint8_t RA8875::getc(void)
00571 {
00572     //#define GETC_DEV      // for development
00573 #ifdef GETC_DEV
00574     uint8_t keyCode1, keyCode2;
00575 #endif
00576     uint8_t keyCode3;
00577     static uint8_t count = 0;
00578     uint8_t col, row;
00579     uint8_t key;
00580 
00581     while (!readable()) {
00582         wait_us(POLLWAITuSec);
00583         // COUNTIDLETIME(POLLWAITuSec);     // As it is voluntary to call the getc and pend. Don't tally it.
00584         if (idle_callback) {
00585             if (external_abort == (*idle_callback)(getc_wait, 0)) {
00586                 return 0;
00587             }
00588         }
00589     }
00590     // read the key press number
00591     uint8_t keyNumReg = ReadCommand(0xC1) & 0x03;
00592     count++;
00593     switch (keyNumReg) {
00594         case 0x01:      // one key
00595             keyCode3 = ReadCommand(0xC2);
00596 #ifdef GETC_DEV
00597             keyCode2 = 0;
00598             keyCode1 = 0;
00599 #endif
00600             break;
00601         case 0x02:      // two keys
00602             keyCode3 = ReadCommand(0xC3);
00603 #ifdef GETC_DEV
00604             keyCode2 = ReadCommand(0xC2);
00605             keyCode1 = 0;
00606 #endif
00607             break;
00608         case 0x03:      // three keys
00609             keyCode3 = ReadCommand(0xC4);
00610 #ifdef GETC_DEV
00611             keyCode2 = ReadCommand(0xC3);
00612             keyCode1 = ReadCommand(0xC2);
00613 #endif
00614             break;
00615         default:         // no keys (key released)
00616             keyCode3 = 0xFF;
00617 #ifdef GETC_DEV
00618             keyCode2 = 0;
00619             keyCode1 = 0;
00620 #endif
00621             break;
00622     }
00623     if (keyCode3 == 0xFF)
00624         key = pKeyMap[0];                    // Key value 0
00625     else {
00626         row = (keyCode3 >> 4) & 0x03;
00627         col = (keyCode3 &  7);
00628         key = row * 5 + col + 1;    // Keys value 1 - 20
00629         if (key > 21) {
00630             key = 21;
00631         }
00632         key = pKeyMap[key];
00633         key |= (keyCode3 & 0x80);   // combine the key held flag
00634     }
00635 #if GETC_DEV // for Development only
00636     SetTextCursor(0, 20);
00637     printf("   Reg: %02x\r\n", keyNumReg);
00638     printf("  key1: %02x\r\n", keyCode1);
00639     printf("  key2: %02x\r\n", keyCode2);
00640     printf("  key3: %02x\r\n", keyCode3);
00641     printf(" count: %02X\r\n", count);
00642     printf("   key: %02X\r\n", key);
00643 #endif
00644     WriteCommand(RA8875_INTC2, 0x10);       // Clear KS status
00645     return key;
00646 }
00647 
00648 
00649 #ifdef PERF_METRICS
00650 void RA8875::ClearPerformance()
00651 {
00652     int i;
00653 
00654     for (i=0; i<METRICCOUNT; i++)
00655         metrics[i] = 0;
00656     idletime_usec = 0;
00657     for (i=0; i<256; i++)
00658         commandsUsed[i] = 0;
00659 }
00660 
00661 
00662 void RA8875::RegisterPerformance(method_e method)
00663 {
00664     unsigned long elapsed = performance.read_us();
00665 
00666     if (method < METRICCOUNT && elapsed > metrics[method])
00667         metrics[method] = elapsed;
00668 }
00669 
00670 
00671 void RA8875::CountIdleTime(uint32_t t)
00672 {
00673     idletime_usec += t;
00674 }
00675 
00676 
00677 void RA8875::ReportPerformance(Serial & pc)
00678 {
00679     int i;
00680 
00681     pc.printf("\r\nPerformance Metrics\r\n");
00682     for (i=0; i<METRICCOUNT; i++) {
00683         pc.printf("%10d uS %s\r\n", metrics[i], metricsName[i]);
00684     }
00685     pc.printf("%10d uS Idle time polling display for ready.\r\n", idletime_usec);
00686     for (i=0; i<256; i++) {
00687         if (commandsUsed[i])
00688             pc.printf("Command %02X used %5d times.\r\n", i, commandsUsed[i]);
00689     }
00690 }
00691 #endif
00692 
00693 
00694 rect_t RA8875::AlignRectInRect(rect_t toAlign, rect_t inRect, valign_t v, halign_t h) {
00695     rect_t newRect; dim_t width; dim_t height;
00696     width = toAlign.p2.x - toAlign.p1.x + 1;
00697     height = toAlign.p2.y - toAlign.p1.y + 1;
00698     INFO("Align(%d,%d)-(%d,%d) in (%d,%d)-(%d,%d) v:%d, h:%d",
00699         toAlign.p1.x,toAlign.p1.y,toAlign.p2.x,toAlign.p2.y,
00700         inRect.p1.x,inRect.p1.y,inRect.p2.x,inRect.p2.y,
00701         v,h
00702         );
00703     switch (h) {
00704         case left:
00705         default:
00706             newRect.p1.x = inRect.p1.x;
00707             newRect.p2.x = newRect.p1.x + width - 1;
00708             break;
00709         case center:
00710             newRect.p1.x = (inRect.p1.x + inRect.p2.x + 1) / 2 - width / 2;
00711             newRect.p2.x = newRect.p1.x + width - 1;
00712             break;
00713         case right:
00714             newRect.p1.x = inRect.p2.x - width - 1;
00715             newRect.p2.x = newRect.p1.x + width - 1;
00716             break;
00717     }
00718     switch (v) {
00719         case top:
00720         default:
00721             newRect.p1.y = inRect.p1.y;
00722             newRect.p2.y = newRect.p1.y + height - 1;
00723             break;
00724         case middle:
00725             newRect.p1.y = (inRect.p1.y + inRect.p2.y + 1) / 2 - height / 2;
00726             newRect.p2.y = newRect.p1.y + height - 1;
00727             break;
00728         case bottom:
00729             newRect.p1.y = inRect.p2.y - height - 1;
00730             newRect.p2.y = newRect.p1.y + height - 1;
00731             break;
00732     }
00733     return newRect;
00734 }
00735 
00736 
00737 bool RA8875::Intersect(rect_t rect, point_t p)
00738 {
00739     if (p.x >= RAmin(rect.p1.x, rect.p2.x) && p.x <= RAmax(rect.p1.x, rect.p2.x)
00740     && p.y >= RAmin(rect.p1.y, rect.p2.y) && p.y <= RAmax(rect.p1.y, rect.p2.y))
00741         return true;
00742     else
00743         return false;
00744 }
00745 
00746 
00747 bool RA8875::Intersect(rect_t rect1, rect_t rect2)
00748 {
00749 #if 1
00750     // If one rectangle is on left side of other
00751     if (RAmax(rect1.p1.x,rect1.p2.x) < RAmin(rect2.p1.x,rect2.p2.x)
00752         || RAmin(rect1.p1.x, rect1.p2.x) > RAmax(rect2.p1.x, rect2.p2.x))
00753         return false;
00754     // If one rectangle is above other
00755     if (RAmax(rect1.p1.y, rect1.p2.y) < RAmin(rect2.p1.y, rect2.p2.y)
00756         || RAmin(rect1.p1.y, rect1.p2.y) > RAmax(rect2.p1.y, rect2.p2.y))
00757         return false;
00758     return true;            // all that's left is they overlap
00759 #else
00760     point_t bl, tr;
00761     bl.x = rect2.p1.x;
00762     bl.y = rect2.p2.y;
00763     tr.x = rect2.p2.x;
00764     tr.y = rect2.p1.y;
00765     if (Intersect(rect1, rect2.p1) || Intersect(rect1, rect2.p2)
00766         || Intersect(rect1, bl) || Intersect(rect1, tr))
00767         return true;
00768     else
00769         return false;
00770 #endif
00771 }
00772 
00773 
00774 bool RA8875::Intersect(rect_t * pRect1, const rect_t * pRect2)
00775 {
00776     if (Intersect(*pRect1, *pRect2)) {
00777         rect_t iSect;
00778 
00779         iSect.p1.x = RAmax(RAmin(pRect1->p1.x,pRect1->p2.x),RAmin(pRect2->p1.x,pRect2->p2.x));
00780         iSect.p1.y = RAmax(RAmin(pRect1->p1.y,pRect1->p2.y),RAmin(pRect2->p1.y,pRect2->p2.y));
00781         iSect.p2.x = RAmin(RAmax(pRect1->p1.x,pRect1->p2.x),RAmax(pRect2->p1.x,pRect2->p2.x));
00782         iSect.p2.y = RAmin(RAmax(pRect1->p1.y,pRect1->p2.y),RAmax(pRect2->p1.y,pRect2->p2.y));
00783         *pRect1 = iSect;
00784         return true;
00785     } else {
00786         return false;
00787     }
00788 }
00789 
00790 
00791 RetCode_t RA8875::WriteCommandW(uint8_t command, uint16_t data)
00792 {
00793     WriteCommand(command, data & 0xFF);
00794     WriteCommand(command+1, data >> 8);
00795     return noerror;
00796 }
00797 
00798 
00799 RetCode_t RA8875::WriteCommand(unsigned char command, unsigned int data)
00800 {
00801 #ifdef PERF_METRICS
00802     if (commandsUsed[command] < 65535)
00803         commandsUsed[command]++;
00804 #endif
00805     _select(true);
00806     _spiwrite(0x80);            // RS:1 (Cmd/Status), RW:0 (Write)
00807     _spiwrite(command);
00808     if (data <= 0xFF) {   // only if in the valid range
00809         _spiwrite(0x00);
00810         _spiwrite(data);
00811     }
00812     _select(false);
00813     return noerror;
00814 }
00815 
00816 
00817 RetCode_t RA8875::WriteDataW(uint16_t data)
00818 {
00819     _select(true);
00820     _spiwrite(0x00);            // RS:0 (Data), RW:0 (Write)
00821     _spiwrite(data & 0xFF);
00822     _spiwrite(data >> 8);
00823     _select(false);
00824     return noerror;
00825 }
00826 
00827 
00828 RetCode_t RA8875::WriteData(unsigned char data)
00829 {
00830     _select(true);
00831     _spiwrite(0x00);            // RS:0 (Data), RW:0 (Write)
00832     _spiwrite(data);
00833     _select(false);
00834     return noerror;
00835 }
00836 
00837 
00838 unsigned char RA8875::ReadCommand(unsigned char command)
00839 {
00840     WriteCommand(command);
00841     return ReadData();
00842 }
00843 
00844 uint16_t RA8875::ReadCommandW(unsigned char command)
00845 {
00846     WriteCommand(command);
00847     return ReadDataW();
00848 }
00849 
00850 unsigned char RA8875::ReadData(void)
00851 {
00852     unsigned char data;
00853 
00854     _select(true);
00855     _spiwrite(0x40);            // RS:0 (Data), RW:1 (Read)
00856     data = _spiread();
00857     _select(false);
00858     return data;
00859 }
00860 
00861 
00862 uint16_t RA8875::ReadDataW(void)
00863 {
00864     uint16_t data;
00865 
00866     _select(true);
00867     _spiwrite(0x40);            // RS:0 (Data), RW:1 (Read)
00868     data  = _spiread();
00869     data |= (_spiread() << 8);
00870     _select(false);
00871     return data;
00872 }
00873 
00874 
00875 unsigned char RA8875::ReadStatus(void)
00876 {
00877     unsigned char data;
00878 
00879     _select(true);
00880     _spiwrite(0xC0);            // RS:1 (Cmd/Status), RW:1 (Read) (Read STSR)
00881     data = _spiread();
00882     _select(false);
00883     return data;
00884 }
00885 
00886 
00887 /// @todo add a timeout and return false, but how long
00888 /// to wait since some operations can be very long.
00889 bool RA8875::_WaitWhileBusy(uint8_t mask)
00890 {
00891     int i = 20000/POLLWAITuSec; // 20 msec max
00892 
00893     while (i-- && ReadStatus() & mask) {
00894         wait_us(POLLWAITuSec);
00895         COUNTIDLETIME(POLLWAITuSec);
00896         if (idle_callback) {
00897             if (external_abort == (*idle_callback)(status_wait, 0)) {
00898                 return false;
00899             }
00900         }
00901     }
00902     if (i)
00903         return true;
00904     else
00905         return false;
00906 }
00907 
00908 
00909 /// @todo add a timeout and return false, but how long
00910 /// to wait since some operations can be very long.
00911 bool RA8875::_WaitWhileReg(uint8_t reg, uint8_t mask)
00912 {
00913     int i = 20000/POLLWAITuSec; // 20 msec max
00914 
00915     while (i-- && ReadCommand(reg) & mask) {
00916         wait_us(POLLWAITuSec);
00917         COUNTIDLETIME(POLLWAITuSec);
00918         if (idle_callback) {
00919             if (external_abort == (*idle_callback)(command_wait, 0)) {
00920                 return false;
00921             }
00922         }
00923     }
00924     if (i)
00925         return true;
00926     else
00927         return false;
00928 }
00929 
00930 // RRRR RGGG GGGB BBBB
00931 // 4321 0543 2104 3210
00932 //           RRRG GGBB
00933 //           2102 1010
00934 uint8_t RA8875::_cvt16to8(color_t c16)
00935 {
00936     return ((c16 >> 8) & 0xE0)
00937         | ((c16 >> 6) & 0x1C)
00938         | ((c16 >> 3) & 0x03);
00939 }
00940 
00941 //           RRRG GGBB
00942 //           2102 1010
00943 // RRRR RGGG GGGB BBBB
00944 // 2101 0543 2104 3210
00945 color_t RA8875::_cvt8to16(uint8_t c8)
00946 {
00947     color_t c16;
00948     color_t temp = (color_t)c8;
00949 
00950     c16 = ((temp & 0xE0) << 8)
00951         | ((temp & 0xC0) << 5)
00952         | ((temp & 0x1C) << 6)
00953         | ((temp & 0x1C) << 3)
00954         | ((temp & 0x03) << 3)
00955         | ((temp & 0x03) << 1)
00956         | ((temp & 0x03) >> 1);
00957     c16 = (c16 << 8) | (c16 >> 8);
00958     return c16;
00959 }
00960 
00961 RetCode_t RA8875::_writeColorTrio(uint8_t regAddr, color_t color)
00962 {
00963     RetCode_t rt = noerror;
00964 
00965     if (screenbpp == 16) {
00966         WriteCommand(regAddr+0, (color>>11));                  // BGCR0
00967         WriteCommand(regAddr+1, (unsigned char)(color>>5));    // BGCR1
00968         rt = WriteCommand(regAddr+2, (unsigned char)(color));       // BGCR2
00969     } else {
00970         uint8_t r, g, b;
00971 
00972         // RRRR RGGG GGGB BBBB      RGB
00973         // RRR   GGG    B B
00974         r = (uint8_t)((color) >> 13);
00975         g = (uint8_t)((color) >> 8);
00976         b = (uint8_t)((color) >> 3);
00977         WriteCommand(regAddr+0, r);  // BGCR0
00978         WriteCommand(regAddr+1, g);  // BGCR1
00979         rt = WriteCommand(regAddr+2, b);  // BGCR2
00980     }
00981     return rt;
00982 }
00983 
00984 color_t RA8875::_readColorTrio(uint8_t regAddr)
00985 {
00986     color_t color;
00987     uint8_t r, g, b;
00988 
00989     r = ReadCommand(regAddr+0);
00990     g = ReadCommand(regAddr+1);
00991     b = ReadCommand(regAddr+2);
00992     if (screenbpp == 16) {
00993         // 000R RRRR 00GG GGGG 000B BBBB
00994         // RRRR RGGG GGGB BBBB
00995         color  = (r & 0x1F) << 11;
00996         color |= (g & 0x3F) << 5;
00997         color |= (b & 0x1F);
00998     } else {
00999         // RRRG GGBB
01000         // RRRR RGGG GGGB BBBB
01001         color  = (r & 0x07) << 13;
01002         color |= (g & 0x07) << 8;
01003         color |= (b & 0x03) << 3;
01004     }
01005     return color;
01006 }
01007 
01008 
01009 dim_t RA8875::fontwidth(void)
01010 {
01011     if (font == NULL)
01012         return (((ReadCommand(RA8875_FNCR1) >> 2) & 0x3) + 1) * 8;
01013     else
01014         return extFontWidth;
01015 }
01016 
01017 
01018 dim_t RA8875::fontheight(void)
01019 {
01020     if (font == NULL)
01021         return (((ReadCommand(RA8875_FNCR1) >> 0) & 0x3) + 1) * 16;
01022     else
01023         return extFontHeight;
01024 }
01025 
01026 
01027 point_t RA8875::locate(textloc_t column, textloc_t row)
01028 {
01029     return SetTextCursor(column * fontwidth(), row * fontheight());
01030 }
01031 
01032 
01033 int RA8875::columns(void)
01034 {
01035     return virt_screenwidth / fontwidth();
01036 }
01037 
01038 
01039 int RA8875::rows(void)
01040 {
01041     return virt_screenheight / fontheight();
01042 }
01043 
01044 
01045 dim_t RA8875::width(void)
01046 {
01047     return virt_screenwidth;
01048 }
01049 
01050 
01051 dim_t RA8875::height(void)
01052 {
01053     return virt_screenheight;
01054 }
01055 
01056 
01057 dim_t RA8875::color_bpp(void)
01058 {
01059     return screenbpp;
01060 }
01061 
01062 bool RA8875::SetWordWrap(bool _wordwrap) {
01063     bool prevWrap = wordwrap;
01064     wordwrap = _wordwrap;
01065     return prevWrap;
01066 }
01067 
01068 point_t RA8875::SetTextCursor(point_t p)
01069 {
01070     return SetTextCursor(p.x, p.y);
01071 }
01072 
01073 point_t RA8875::SetTextCursor(loc_t x, loc_t y)
01074 {
01075     point_t oldCursor = GetTextCursor();
01076     if (x >= 0 && x <= virt_screenwidth
01077         && y >= 0 && y <= virt_screenheight) {
01078         cursor_x = x;     // set these values for non-internal fonts
01079         cursor_y = y;
01080         switch (screen_orientation) {
01081             default:
01082             case rotate_0:
01083             case rotate_180:
01084                 WriteCommandW(RA8875_FCURXL, x);
01085                 WriteCommandW(RA8875_FCURYL, y);
01086                 break;
01087             case rotate_90:
01088             case rotate_270:
01089                 WriteCommandW(RA8875_FCURXL, y);
01090                 WriteCommandW(RA8875_FCURYL, x);
01091                 break;
01092         }
01093     }
01094     return oldCursor;
01095 }
01096 
01097 point_t RA8875::GetTextCursor(void)
01098 {
01099     point_t p, tempPt;
01100 
01101     if (font == NULL) {
01102         p.x = ReadCommand(RA8875_FCURXL) | (ReadCommand(RA8875_FCURXH) << 8);
01103         p.y = ReadCommand(RA8875_FCURYL) | (ReadCommand(RA8875_FCURYH) << 8);
01104         switch (screen_orientation) {
01105         case rotate_0:
01106         case rotate_180:
01107             // Nothing
01108             break;
01109         case rotate_90:
01110         case rotate_270:
01111             tempPt.x = p.y;
01112             tempPt.y = p.x;
01113             p = tempPt;
01114             break;
01115         default:
01116             p.x = 0;
01117             p.y = 0;
01118             break;
01119         }
01120     } else {
01121         p.x = cursor_x;
01122         p.y = cursor_y;
01123     }
01124     //INFO("GetCursor hw: (%d,%d)", p.x, p.y);
01125     return p;
01126 }
01127 
01128 loc_t RA8875::GetTextCursor_Y(void)
01129 {
01130     point_t p = GetTextCursor();
01131     INFO("GetTextCursor_Y = %d", p.y);
01132     return p.y;
01133 }
01134 
01135 
01136 loc_t RA8875::GetTextCursor_X(void)
01137 {
01138     point_t p = GetTextCursor();
01139     INFO("GetTextCursor_X = %d", p.x);
01140     return p.x;
01141 }
01142 
01143 
01144 RetCode_t RA8875::SetTextCursorControl(cursor_t cursor, bool blink)
01145 {
01146     unsigned char mwcr0 = ReadCommand(RA8875_MWCR0) & 0x0F; // retain direction, auto-increase
01147     unsigned char mwcr1 = ReadCommand(RA8875_MWCR1) & 0x01; // retain currently selected layer
01148     unsigned char horz = 0;
01149     unsigned char vert = 0;
01150 
01151     mwcr0 |= 0x80;                          // text mode
01152     if (cursor != NOCURSOR)
01153         mwcr0 |= 0x40;                      // visible
01154     if (blink)
01155         mwcr0 |= 0x20;                      // blink
01156     WriteCommand(RA8875_MWCR0, mwcr0);      // configure the cursor
01157     WriteCommand(RA8875_MWCR1, mwcr1);      // close the graphics cursor
01158     WriteCommand(RA8875_BTCR, 0x1f);        // The cursor flashing cycle
01159     switch (cursor) {
01160         case IBEAM:
01161             horz = 0x01;
01162             vert = 0x1F;
01163             break;
01164         case UNDER:
01165             horz = 0x07;
01166             vert = 0x01;
01167             break;
01168         case BLOCK:
01169             horz = 0x07;
01170             vert = 0x1F;
01171             break;
01172         case NOCURSOR:
01173         default:
01174             break;
01175     }
01176     WriteCommand(RA8875_CURHS, horz);       // The cursor size horz
01177     WriteCommand(RA8875_CURVS, vert);       // The cursor size vert
01178     return noerror;
01179 }
01180 
01181 
01182 RetCode_t RA8875::SetTextFont(RA8875::font_t font)
01183 {
01184     if (/*font >= RA8875::ISO8859_1 && */ font <= RA8875::ISO8859_4) {
01185         WriteCommand(RA8875_FNCR0, (unsigned int)(font));
01186         return noerror;
01187     } else {
01188         return bad_parameter;
01189     }
01190 }
01191 
01192 point_t RA8875::TranslateOrientation(loc_t x, loc_t y) {
01193     point_t pt;
01194 
01195     switch (screen_orientation) {
01196         default:
01197         case rotate_0:
01198             pt.x = x;
01199             pt.y = y;
01200             break;
01201         case rotate_90:
01202             pt.x = y;
01203             pt.y = virt_screenheight - 1 - x;
01204             break;
01205         case rotate_180:
01206             pt.x = virt_screenwidth - 1 - x;
01207             pt.y = virt_screenheight - 1 - y;
01208             break;
01209         case rotate_270:
01210             pt.x = virt_screenwidth - 1 - y;
01211             pt.y = x;
01212             break;
01213     }
01214     return pt;
01215 }
01216 
01217 
01218 orientation_t RA8875::GetGraphicsOrientation()
01219 {
01220     return screen_orientation;
01221 }
01222 
01223 orientation_t RA8875::SetGraphicsOrientation(orientation_t angle)
01224 {
01225     uint8_t dpcrVal = ReadCommand(RA8875_DPCR);
01226     uint8_t mwcr0 = ReadCommand(RA8875_MWCR0);
01227     uint8_t mrcd = ReadCommand(RA8875_MRCD);
01228     dim_t tempWidth, tempHeight;
01229     orientation_t oldAngle = screen_orientation;
01230 
01231     dpcrVal &= ~0x0C;       // remove the old scan direction bits
01232     mwcr0 &= 0xE3;          // remove the old direction bits
01233     mrcd &= 0xFC;           // remove the old direction bits
01234     switch (angle) {
01235         case normal:
01236             //dpcrVal |= 0x00;
01237             tempWidth = RAmax(virt_screenwidth, virt_screenheight);
01238             tempHeight = RAmin(virt_screenwidth, virt_screenheight);
01239             break;
01240         case rotate_90:
01241             dpcrVal |= 0x08;
01242             mwcr0 |= 0x08;
01243             mrcd |= 0x02;
01244             tempWidth = RAmin(virt_screenwidth, virt_screenheight);
01245             tempHeight = RAmax(virt_screenwidth, virt_screenheight);
01246             break;
01247         case rotate_180:
01248             dpcrVal |= 0x0C;
01249             //mwcr0 |= 0x00;
01250             //mrcd |= 0x00;
01251             tempWidth = RAmax(virt_screenwidth, virt_screenheight);
01252             tempHeight = RAmin(virt_screenwidth, virt_screenheight);
01253             break;
01254         case rotate_270:
01255             dpcrVal |= 0x04;
01256             mwcr0 |= 0x08;
01257             mrcd |= 0x02;
01258             tempWidth = RAmin(virt_screenwidth, virt_screenheight);
01259             tempHeight = RAmax(virt_screenwidth, virt_screenheight);
01260             break;
01261         default:
01262             return invalid;
01263     }
01264     screen_orientation = angle;
01265     virt_screenwidth = tempWidth;
01266     virt_screenheight = tempHeight;
01267     WriteCommand(RA8875_MRCD, mrcd);
01268     WriteCommand(RA8875_MWCR0, mwcr0);
01269     WriteCommand(RA8875_DPCR, dpcrVal);
01270     return oldAngle;
01271 }
01272 
01273 orientation_t RA8875::SetTextFontOrientation(orientation_t angle)
01274 {
01275     uint8_t fncr1Val = ReadCommand(RA8875_FNCR1);
01276     orientation_t oldAngle = text_orientation;
01277 
01278     fncr1Val &= ~0x10;      // remove the old direction bit
01279     switch (angle) {
01280     case normal:
01281         //fncr1Val |= 0x10;
01282         break;
01283     case rotate_90:
01284         fncr1Val |= 0x10;
01285         break;
01286     case rotate_180:
01287         //fncr1Val |= 0x00;
01288         break;
01289     case rotate_270:
01290         fncr1Val |= 0x10;
01291         break;
01292     default:
01293         return invalid;
01294     }
01295     WriteCommand(RA8875_FNCR1, fncr1Val);
01296     text_orientation = angle;
01297     return oldAngle;
01298 }
01299 
01300 RetCode_t RA8875::SetTextFontControl(fill_t fillit,
01301                                      RA8875::HorizontalScale hScale,
01302                                      RA8875::VerticalScale vScale,
01303                                      RA8875::alignment_t alignment)
01304 {
01305     if (hScale >= 1 && hScale <= 4 &&
01306             vScale >= 1 && vScale <= 4) {
01307         uint8_t fncr1Val = ReadCommand(RA8875_FNCR1);
01308 
01309         fncr1Val &= 0x10;      // remove all except the font rotation bit
01310         if (alignment == align_full)
01311             fncr1Val |= 0x80;
01312         if (fillit == NOFILL)
01313             fncr1Val |= 0x40;
01314         fncr1Val |= ((hScale - 1) << 2);
01315         fncr1Val |= ((vScale - 1) << 0);
01316         return WriteCommand(RA8875_FNCR1, fncr1Val);
01317     } else {
01318         return bad_parameter;
01319     }
01320 }
01321 
01322 fill_t RA8875::SetTextFontFill(fill_t fillit)
01323 {
01324     fill_t prevFill = FILL;
01325     uint8_t fncr1Val = ReadCommand(RA8875_FNCR1);
01326 
01327     if (fncr1Val & 0x40)
01328         prevFill = NOFILL;
01329     fncr1Val &= ~0x40;
01330     if (fillit == NOFILL)
01331         fncr1Val |= 0x40;
01332     WriteCommand(RA8875_FNCR1, fncr1Val);
01333     return prevFill;
01334 }
01335 
01336 RetCode_t RA8875::SetTextFontSize(RA8875::HorizontalScale hScale, RA8875::VerticalScale vScale)
01337 {
01338     unsigned char reg = ReadCommand(RA8875_FNCR1);
01339 
01340     if (vScale == -1)
01341         vScale = hScale;
01342     if (hScale >= 1 && hScale <= 4 && vScale >= 1 && vScale <= 4) {
01343         fontScaleX = hScale;            // save for use with a Soft Font
01344         fontScaleY = vScale;
01345         reg &= 0xF0;                    // keep the high nibble as is.
01346         reg |= ((hScale - 1) << 2);
01347         reg |= ((vScale - 1) << 0);
01348         WriteCommand(RA8875_FNCR1, reg);
01349         return noerror;
01350     } else {
01351         return bad_parameter;
01352     }
01353 }
01354 
01355 RetCode_t RA8875::GetTextFontSize(RA8875::HorizontalScale * hScale, RA8875::VerticalScale * vScale)
01356 {
01357     unsigned char reg = ReadCommand(RA8875_FNCR1);
01358 
01359     if (hScale)
01360         *hScale = 1 + ((reg >> 2) & 0x03);
01361     if (vScale)
01362         *vScale = 1 + (reg & 0x03);
01363     return noerror;
01364 }
01365 
01366 dim_t RA8875::GetTextWidth(const char * text, bool charOnly)
01367 {
01368     if (font == NULL) {
01369         if (charOnly)
01370             return fontwidth();
01371         else
01372             return fontwidth() * strlen(text);
01373     } else {
01374         dim_t width = 0;
01375 
01376         while (*text) {
01377             dim_t cWidth;
01378             if (getCharMetrics(*text, &cWidth, NULL))
01379                 width += cWidth;
01380             if (charOnly)
01381                 break;
01382             text++;
01383         }
01384         return width * fontScaleX;
01385     }
01386 }
01387 
01388 rect_t RA8875::GetTextRect(const char* text, bool charOnly) {
01389     rect_t rect = { 0, 0, 0, 0 };
01390 
01391     if (charOnly) {
01392         if (font == NULL) {
01393             rect.p2.x = fontwidth();
01394             rect.p2.y = fontheight();
01395         } else {
01396             dim_t cWidth, cHeight;
01397             if (getCharMetrics(*text, &cWidth, &cHeight)) {
01398                 rect.p2.x = cWidth;
01399                 rect.p2.y = cHeight;
01400             }
01401         }
01402     } else {
01403         rect.p2.y = fontheight();
01404         const char* p = text;
01405         dim_t curX = 0;
01406         while (*p) {
01407             rect_t cRect = GetTextRect(p, true);
01408             switch (*p) {
01409                 case '\r':
01410                     curX = 0;
01411                     break;
01412                 case '\n':
01413                     rect.p2.y += cRect.p2.y;
01414                     break;
01415                 default:
01416                     if ((curX + cRect.p2.x) >= virt_screenwidth) {
01417                         rect.p2.x = virt_screenwidth - 1;
01418                         curX = cRect.p2.x;
01419                         if ((rect.p2.y + cRect.p2.y) > virt_screenheight) {
01420                             rect.p2.y = virt_screenheight - 1;
01421                         } else {
01422                             rect.p2.y += cRect.p2.y;
01423                         }
01424                     } else {
01425                         curX += cRect.p2.x;
01426                         if (curX > rect.p2.x)
01427                             rect.p2.x = curX;
01428                     }
01429                     break;
01430             }
01431             if (rect.p2.y >= virt_screenheight - 1)
01432                 break;
01433             p++;
01434         }
01435     }
01436     return rect;
01437 }
01438 
01439 
01440 int RA8875::_putc(int c)
01441 {
01442     if (font == NULL) {
01443         return _internal_putc(c);
01444     } else {
01445         return _external_putc(c);
01446     }
01447 }
01448 
01449 
01450 
01451 // Questions to ponder -
01452 // - if we choose to wrap to the next line, because the character won't fit on the current line,
01453 //      should it erase the space to the width of the screen (in case there is leftover junk there)?
01454 // - it currently wraps from the bottom of the screen back to the top. I have pondered what
01455 //      it might take to scroll the screen - but haven't thought hard enough about it.
01456 //
01457 int RA8875::_external_putc(int c)
01458 {
01459     if (c) {
01460         if (c == '\r') {
01461             cursor_x = windowrect.p1.x;
01462         } else if (c == '\n') {
01463             cursor_y += extFontHeight;
01464         } else {
01465             dim_t charWidth, charHeight;
01466             const uint8_t * charRecord = getCharMetrics(c, &charWidth, &charHeight);
01467             if (charRecord) {
01468                 //cursor_x += advance;
01469                 if (cursor_x + charWidth >= windowrect.p2.x) {
01470                     cursor_x = windowrect.p1.x;
01471                     cursor_y += charHeight;
01472                 }
01473                 if (cursor_y + charHeight >= windowrect.p2.y) {
01474                     cursor_y = windowrect.p1.y;               // @todo Should it scroll?
01475                 }
01476                 (void)character(cursor_x, cursor_y, c);
01477                 cursor_x += charWidth * fontScaleX;
01478             }
01479         }
01480     }
01481     return c;
01482 }
01483 
01484 
01485 int RA8875::_internal_putc(int c)
01486 {
01487     if (c) {
01488         unsigned char mwcr0 = ReadCommand(RA8875_MWCR0);
01489         if ((mwcr0 & 0x80) == 0x00)
01490             WriteCommand(RA8875_MWCR0, 0x80 | mwcr0);    // Put in Text mode if not already
01491         if (c == '\r' || c == '\n') {
01492             point_t cur = GetTextCursor();
01493             if (c == '\r') {
01494                 cur.x = windowrect.p1.x;
01495             } else if (c == '\n') {
01496                 cur.y += fontheight();
01497                 if (cur.y >= windowrect.p2.y)               // @TODO after bottom of active window, then scroll window?
01498                     cur.y = windowrect.p1.y;
01499             }
01500             SetTextCursor(cur);
01501         } else {
01502             WriteCommand(RA8875_MRWC);                 // RA8875 Internal Fonts
01503             _select(true);
01504             WriteData(c);
01505             _WaitWhileBusy(0x80);
01506             _select(false);
01507         }
01508     }
01509     return c;
01510 }
01511 
01512 
01513 RetCode_t RA8875::_StartGraphicsStream(void)
01514 {
01515     unsigned char mwcr0;
01516 
01517     mwcr0 = ReadCommand(RA8875_MWCR0) & ~0x80;
01518     mwcr0 |= 0x00;
01519     //INFO("_StartGraphicsStream mwcr0: %02X", mwcr0);
01520     WriteCommand(RA8875_MWCR0, mwcr0);      // Graphics write mode
01521     WriteCommand(RA8875_MRWC);              // Prepare for streaming data
01522     return noerror;
01523 }
01524 
01525 
01526 RetCode_t RA8875::_EndGraphicsStream(void)
01527 {
01528     return noerror;
01529 }
01530 
01531 
01532 RetCode_t RA8875::_putp(color_t pixel)
01533 {
01534     WriteDataW((pixel>>8) | (pixel<<8));
01535     return noerror;
01536 }
01537 
01538 
01539 void RA8875::puts(point_t pt, const char * string)
01540 {
01541     SetTextCursor(pt);
01542     puts(string);
01543 }
01544 
01545 void RA8875::puts(loc_t x, loc_t y, const char * string)
01546 {
01547     SetTextCursor(x,y);
01548     puts(string);
01549 }
01550 
01551 
01552 void RA8875::puts(const char * string)
01553 {
01554     INFO("puts len(%d), (%s)", strlen(string), string);
01555     if (font == NULL) {
01556         unsigned char mwcr0 = ReadCommand(RA8875_MWCR0);
01557         if ((mwcr0 & 0x80) == 0x00)
01558             WriteCommand(RA8875_MWCR0, 0x80 | mwcr0);    // Put in Text mode if not already
01559     }
01560     while (string && *string) {           // @TODO calling individual _putc is slower... optimizations?
01561         if (wordwrap) {
01562             const char * p = string;
01563             const char * pSpace = NULL;
01564             dim_t txtPos;
01565             bool newLineNeeded = false;
01566 
01567             point_t cursor = GetTextCursor();
01568             //INFO("cursor (%d,%d)", cursor.x, cursor.y);
01569             txtPos = cursor.x;
01570             INFO("(%d,%d) string: '%s'", cursor.x, cursor.y, string);
01571             // find what fits in the window
01572             do {
01573                 if (*p == ' ')
01574                     pSpace = p;
01575                 if (*p == '\r') {
01576                     pSpace = p;
01577                     break;
01578                 }
01579                 if (*p == '\n') {
01580                     break;
01581                 }
01582                 dim_t cWidth = GetTextWidth(p, true);
01583                 INFO("txtPos: %d, cWidth: %d, p2.x: %d", txtPos, cWidth, windowrect.p2.x);
01584                 if (txtPos + cWidth <= windowrect.p2.x) {
01585                     txtPos += cWidth;
01586                 } else {
01587                     newLineNeeded = true;
01588                     break;
01589                 }
01590                 INFO("+ %c width %d, ttl Width %d <= %d", *p, cWidth, txtPos, windowrect.p2.x);
01591                 p++;
01592             } while (*p);
01593             INFO("p: len(%d), (%s)", strlen(p), p);
01594             if (*p && *p != ' ' && pSpace) {
01595                 p = pSpace;
01596                 INFO("p: len(%d), (%s)", strlen(p), p);
01597             }
01598             char dbgBuf[256];
01599             strncpy(dbgBuf, string, (p - string));
01600             dbgBuf[p - string] = '\0';
01601             INFO("chunk: %d <= w.p2.x: %d - '%s'", txtPos, windowrect.p2.x, dbgBuf);
01602             if (txtPos <= windowrect.p2.x) {
01603                 while (*string && string <= p) {
01604                     INFO("*string %02X", *string);
01605                     if (*string == '\r') {
01606                         cursor = GetTextCursor();
01607                         SetTextCursor(windowrect.p1.x, cursor.y);
01608                     } else if (*string == '\n') {
01609                         cursor = GetTextCursor();
01610                         SetTextCursor(cursor.x, cursor.y + fontheight());
01611                     } else {
01612                         INFO("cursor (%d,%d) - %c", cursor.x, cursor.y, *string);
01613                         _putc(*string);
01614                     }
01615                     string++;
01616                 }
01617             }
01618             while (*string == ' ')
01619                 string++;
01620             if (newLineNeeded) {
01621                 cursor.y += fontheight();
01622                 SetTextCursor(cursor);
01623                 INFO("\r\n");
01624             }
01625             INFO("### '%s'\r\n\r\n", string);
01626             // if != ' ', find ' ', accumulating width along the way
01627             // if the width fits, print the chars
01628         } else {
01629             _putc(*string++);
01630         }
01631     }
01632     //point_t cursor = GetTextCursor();
01633     //INFO("cursor (%d,%d)", cursor.x, cursor.y);
01634 }
01635 
01636 
01637 RetCode_t RA8875::SetGraphicsCursor(loc_t x, loc_t y)
01638 {
01639     switch (screen_orientation) {
01640     case rotate_0:
01641     case rotate_180:
01642         WriteCommandW(RA8875_CURH0, x);
01643         WriteCommandW(RA8875_CURV0, y);
01644         break;
01645     case rotate_90:
01646     case rotate_270:
01647         WriteCommandW(RA8875_CURH0, y);
01648         WriteCommandW(RA8875_CURV0, x);
01649         break;
01650     case invalid:
01651         return bad_parameter;
01652     }
01653     return noerror;
01654 }
01655 
01656 RetCode_t RA8875::SetGraphicsCursor(point_t p)
01657 {
01658     return SetGraphicsCursor(p.x, p.y);
01659 }
01660 
01661 point_t RA8875::GetGraphicsCursor(void)
01662 {
01663     point_t p;
01664 
01665     switch (screen_orientation) {
01666     case rotate_0:
01667     case rotate_180:
01668         p.x = ReadCommandW(RA8875_CURH0);
01669         p.y = ReadCommandW(RA8875_CURV0);
01670         break;
01671     case rotate_90:
01672     case rotate_270:
01673         p.y = ReadCommandW(RA8875_CURH0);
01674         p.x = ReadCommandW(RA8875_CURV0);
01675         break;
01676     case invalid:
01677         p.x = p.y = 0;
01678         break;
01679     }
01680     return p;
01681 }
01682 
01683 RetCode_t RA8875::SetGraphicsCursorRead(loc_t x, loc_t y)
01684 {
01685     switch (screen_orientation) {
01686     case rotate_0:
01687     case rotate_180:
01688         WriteCommandW(RA8875_RCURH0, x);
01689         WriteCommandW(RA8875_RCURV0, y);
01690         break;
01691     case rotate_90:
01692     case rotate_270:
01693         WriteCommandW(RA8875_RCURH0, y);
01694         WriteCommandW(RA8875_RCURV0, x);
01695         break;
01696     case invalid:
01697         return bad_parameter;
01698     }
01699     return noerror;
01700 }
01701 
01702 
01703 rect_t RA8875::GetWindow() {
01704     return windowrect;
01705 }
01706 
01707 rect_t RA8875::SetWindow(rect_t r)
01708 {
01709     return SetWindow(r.p1.x, r.p1.y, r.p2.x, r.p2.y);
01710 }
01711 
01712 rect_t RA8875::SetWindow(loc_t x1, loc_t y1, loc_t x2, loc_t y2)
01713 {
01714     rect_t nullRect = { 0,0,0,0 };    // in case it is invalid
01715     rect_t oldWin = windowrect;
01716     dim_t tempWidth, tempHeight;
01717     nullRect.p2.x = virt_screenwidth - 1;
01718     nullRect.p2.y = virt_screenheight - 1;
01719     //INFO("SetWindow(%d,%d)-(%d,%d)", x1,y1, x2,y2);
01720     // Correct the initial values (0,-1) - (0,-1)
01721     if (x2 == (loc_t)-1) x2 = virt_screenwidth - 1;
01722     if (y2 == (loc_t)-1) y2 = virt_screenheight - 1;
01723     // Sanity check the parameters.
01724     if (x1 < 0 || y1 < 0 || x2 < 0 || y2 < 0
01725         || x1 >= virt_screenwidth || y1 >= virt_screenheight 
01726         || x2 >= virt_screenwidth || y2 >= virt_screenheight) {
01727         return nullRect;
01728     }
01729     windowrect.p1.x = x1;
01730     windowrect.p1.y = y1;
01731     windowrect.p2.x = x2;
01732     windowrect.p2.y = y2;
01733     switch (screen_orientation) {
01734     case rotate_0:
01735     case rotate_180:
01736         tempWidth = RAmax(virt_screenwidth, virt_screenheight);
01737         tempHeight = RAmin(virt_screenwidth, virt_screenheight);
01738         virt_screenwidth = tempWidth;
01739         virt_screenheight = tempHeight;
01740         //GraphicsDisplay::SetWindow(x1, y1, x2, y2);
01741         WriteCommandW(RA8875_HSAW0, x1);
01742         WriteCommandW(RA8875_VSAW0, y1);
01743         WriteCommandW(RA8875_HEAW0, x2);
01744         WriteCommandW(RA8875_VEAW0, y2);
01745         break;
01746     case rotate_90:
01747     case rotate_270:
01748         tempWidth = RAmin(virt_screenwidth, virt_screenheight);
01749         tempHeight = RAmax(virt_screenwidth, virt_screenheight);
01750         virt_screenwidth = tempWidth;
01751         virt_screenheight = tempHeight;
01752         //GraphicsDisplay::SetWindow(x1, y1, x2, y2);
01753         WriteCommandW(RA8875_HSAW0, y1);
01754         WriteCommandW(RA8875_VSAW0, x1);
01755         WriteCommandW(RA8875_HEAW0, y2);
01756         WriteCommandW(RA8875_VEAW0, x2);
01757         break;
01758     case invalid:
01759         break;
01760     }
01761     return oldWin;
01762 }
01763 
01764 RetCode_t RA8875::cls(uint16_t layers)
01765 {
01766     RetCode_t ret = noerror;
01767 
01768     INFO("cls(%d)", layers);
01769     PERFORMANCE_RESET;
01770     if (layers == 0) {
01771         ret = clsw(FULLWINDOW);
01772     } else if (layers > 3) {
01773         ret = bad_parameter;
01774     } else {
01775         uint16_t prevLayer = GetDrawingLayer();
01776         if (layers & 1) {
01777             SelectDrawingLayer(0);
01778             clsw(FULLWINDOW);
01779         }
01780         if (layers & 2) {
01781             SelectDrawingLayer(1);
01782             clsw(FULLWINDOW);
01783         }
01784         SelectDrawingLayer(prevLayer);
01785     }
01786     SetTextCursor(0,0);
01787     //ret = locate(0,0);
01788     REGISTERPERFORMANCE(PRF_CLS);
01789     return ret;
01790 }
01791 
01792 
01793 RetCode_t RA8875::clsw(RA8875::Region_t region)
01794 {
01795     INFO("clsw(%d)", region);
01796     PERFORMANCE_RESET;
01797     WriteCommand(RA8875_MCLR, (region == ACTIVEWINDOW) ? 0xC0 : 0x80);
01798     if (!_WaitWhileReg(RA8875_MCLR, 0x80)) {
01799         WriteCommand(RA8875_MCLR, 0x00);    // Abort clearing on request
01800         REGISTERPERFORMANCE(PRF_CLS);
01801         return external_abort;
01802     }
01803     REGISTERPERFORMANCE(PRF_CLS);
01804     return noerror;
01805 }
01806 
01807 
01808 RetCode_t RA8875::pixel(point_t p, color_t color)
01809 {
01810     return pixel(p.x, p.y, color);
01811 }
01812 
01813 RetCode_t RA8875::pixel(point_t p)
01814 {
01815     return pixel(p.x, p.y);
01816 }
01817 
01818 RetCode_t RA8875::pixel(loc_t x, loc_t y, color_t color)
01819 {
01820     RetCode_t ret;
01821 
01822     PERFORMANCE_RESET;
01823     ret = pixelStream(&color, 1, x,y);
01824     REGISTERPERFORMANCE(PRF_DRAWPIXEL);
01825     return ret;
01826 }
01827 
01828 
01829 RetCode_t RA8875::pixel(loc_t x, loc_t y)
01830 {
01831     RetCode_t ret;
01832 
01833     PERFORMANCE_RESET;
01834     color_t color = GetForeColor();
01835     ret = pixelStream(&color, 1, x, y);
01836     REGISTERPERFORMANCE(PRF_DRAWPIXEL);
01837     return ret;
01838 }
01839 
01840 
01841 RetCode_t RA8875::pixelStream(color_t * p, uint32_t count, loc_t x, loc_t y)
01842 {
01843     PERFORMANCE_RESET;
01844     SetGraphicsCursor(x, y);
01845     _StartGraphicsStream();
01846     _select(true);
01847     _spiwrite(0x00);         // Cmd: write data
01848     while (count--) {
01849         if (screenbpp == 16) {
01850             _spiwrite(*p >> 8);
01851             _spiwrite(*p & 0xFF);
01852         } else {
01853             _spiwrite(_cvt16to8(*p));
01854         }
01855         p++;
01856     }
01857     _select(false);
01858     _EndGraphicsStream();
01859     REGISTERPERFORMANCE(PRF_PIXELSTREAM);
01860     return(noerror);
01861 }
01862 
01863 // With a font scale X = 1, a pixel stream is "abcdefg..."
01864 // With a font scale X = 2, a pixel stream is "aabbccddeeffgg..."
01865 // With a font scale Y = 2, a pixel stream is "abcdefg..."
01866 //                                            "abcdefg..."
01867 //
01868 RetCode_t RA8875::booleanStream(loc_t x, loc_t y, dim_t w, dim_t h, const uint8_t * boolStream)
01869 {
01870     PERFORMANCE_RESET;
01871     const uint8_t * rowStream = boolStream;
01872     loc_t x2,y2;
01873     x2 = x + w * fontScaleX - 1;
01874     y2 = y + h * fontScaleY - 1;
01875     if (x2 >= virt_screenwidth) {
01876         x -= (x2 - virt_screenwidth + 1);
01877         x2 = virt_screenwidth - 1;
01878     }
01879     if (y2 >= virt_screenheight) {
01880         y -= (y2 - virt_screenheight + 1);
01881         y2 = virt_screenheight - 1;
01882     }
01883     rect_t restore = SetWindow(x, y, x2, y2);
01884     SetGraphicsCursor(x, y);
01885     _StartGraphicsStream();
01886     _select(true);
01887     _spiwrite(0x00);         // Cmd: write data
01888     while (h--) {
01889         for (int dy=0; dy<fontScaleY; dy++) {           // Vertical Font Scale Factor
01890             uint8_t pixels = w;
01891             uint8_t bitmask = 0x01;
01892             rowStream = boolStream;
01893             while (pixels) {
01894                 uint8_t byte = *rowStream;
01895                 //INFO("byte, mask: %02X, %02X", byte, bitmask);
01896                 color_t c = (byte & bitmask) ? _foreground : _background;
01897 
01898                 for (int dx=0; dx<fontScaleX; dx++) {   // Horizontal Font Scale Factor
01899                     if (screenbpp == 16) {
01900                         _spiwrite(c >> 8);
01901                         _spiwrite(c & 0xFF);
01902                     } else {
01903                         _spiwrite(_cvt16to8(c));
01904                     }
01905                 }
01906                 bitmask <<= 1;
01907                 if (pixels > 1 && bitmask == 0) {
01908                     bitmask = 0x01;
01909                     rowStream++;
01910                 }
01911                 pixels--;
01912             }
01913         }
01914         boolStream += (rowStream - boolStream + 1);
01915     }
01916     _select(false);
01917     _EndGraphicsStream();
01918     SetWindow(restore);
01919     REGISTERPERFORMANCE(PRF_BOOLSTREAM);
01920     return(noerror);
01921 }
01922 
01923 color_t RA8875::getPixel(loc_t x, loc_t y)
01924 {
01925     color_t pixel;
01926     unsigned char mwcr0;
01927 
01928     PERFORMANCE_RESET;
01929     mwcr0 = ReadCommand(RA8875_MWCR0);
01930     if (mwcr0 & 0x80)
01931         WriteCommand(RA8875_MWCR0, mwcr0 & ~0x80);    // Graphics write mode
01932     SetGraphicsCursorRead(x, y);
01933     WriteCommand(RA8875_MRWC);
01934     _select(true);
01935     _spiwrite(0x40);         // Cmd: read data
01936     _spiwrite(0x00);         // dummy read
01937     if (screenbpp == 16) {
01938         pixel  = _spiread();
01939         pixel |= (_spiread() << 8);
01940     } else {
01941         pixel = _cvt8to16(_spiread());
01942     }
01943     _select(false);
01944     REGISTERPERFORMANCE(PRF_READPIXEL);
01945     return pixel;
01946 }
01947 
01948 
01949 RetCode_t RA8875::getPixelStream(color_t * p, uint32_t count, loc_t x, loc_t y)
01950 {
01951     color_t pixel;
01952     unsigned char mwcr0;
01953     RetCode_t ret = noerror;
01954 
01955     PERFORMANCE_RESET;
01956     mwcr0 = ReadCommand(RA8875_MWCR0);
01957     if (mwcr0 & 0x80)
01958         WriteCommand(RA8875_MWCR0, mwcr0 & ~0x80);    // Graphics write mode
01959     ret = SetGraphicsCursorRead(x, y);
01960     ret = WriteCommand(RA8875_MRWC);
01961     _select(true);
01962     _spiwrite(0x40);         // Cmd: read data
01963     _spiwrite(0x00);         // dummy read
01964     if (screenbpp == 16)
01965         _spiwrite(0x00);     // dummy read is only necessary when in 16-bit mode
01966     while (count--) {
01967         if (screenbpp == 16) {
01968             pixel  = _spiread();
01969             pixel |= (_spiread() << 8);
01970         } else {
01971             pixel = _cvt8to16(_spiread());
01972         }
01973         *p++ = pixel;
01974     }
01975     _select(false);
01976     REGISTERPERFORMANCE(PRF_READPIXELSTREAM);
01977     return ret;
01978 }
01979 
01980 
01981 RetCode_t RA8875::line(point_t p1, point_t p2)
01982 {
01983     return line(p1.x, p1.y, p2.x, p2.y);
01984 }
01985 
01986 
01987 RetCode_t RA8875::line(point_t p1, point_t p2, color_t color)
01988 {
01989     return line(p1.x, p1.y, p2.x, p2.y, color);
01990 }
01991 
01992 
01993 RetCode_t RA8875::line(loc_t x1, loc_t y1, loc_t x2, loc_t y2, color_t color)
01994 {
01995     foreground(color);
01996     return line(x1,y1,x2,y2);
01997 }
01998 
01999 
02000 RetCode_t RA8875::line(loc_t x1, loc_t y1, loc_t x2, loc_t y2)
02001 {
02002     PERFORMANCE_RESET;
02003     if (x1 == x2 && y1 == y2) {
02004         switch (screen_orientation) {
02005         case invalid:
02006             return bad_parameter;
02007             //break;
02008         case rotate_0:
02009         case rotate_180:
02010             pixel(x1, y1);
02011             break;
02012         case rotate_90:
02013         case rotate_270:
02014             pixel(y1, x1);
02015             break;
02016         }
02017     } else {
02018         switch (screen_orientation) {
02019         case invalid:
02020             return bad_parameter;
02021             //break;
02022         case rotate_0:
02023         case rotate_180:
02024             WriteCommandW(RA8875_DLHSR0, x1);
02025             WriteCommandW(RA8875_DLVSR0, y1);
02026             WriteCommandW(RA8875_DLHER0, x2);
02027             WriteCommandW(RA8875_DLVER0, y2);
02028             break;
02029         case rotate_90:
02030         case rotate_270:
02031             WriteCommandW(RA8875_DLHSR0, y1);
02032             WriteCommandW(RA8875_DLVSR0, x1);
02033             WriteCommandW(RA8875_DLHER0, y2);
02034             WriteCommandW(RA8875_DLVER0, x2);
02035             break;
02036         }
02037         unsigned char drawCmd = 0x00;       // Line
02038         WriteCommand(RA8875_DCR, drawCmd);
02039         WriteCommand(RA8875_DCR, 0x80 + drawCmd); // Start drawing.
02040         if (!_WaitWhileReg(0x90, 0x80)) {
02041             REGISTERPERFORMANCE(PRF_DRAWLINE);
02042             return external_abort;
02043         }
02044     }
02045     REGISTERPERFORMANCE(PRF_DRAWLINE);
02046     return noerror;
02047 }
02048 
02049 
02050 RetCode_t RA8875::ThickLine(point_t p1, point_t p2, dim_t thickness, color_t color)
02051 {
02052     //INFO("ThickLine()");
02053     if (thickness == 1) {
02054         line(p1,p2, color);
02055     } else {
02056         if (p1.x == p2.x) {
02057             // vertical
02058             if (roundCap) {
02059                 fillcircle(p1, thickness / 2, color);
02060                 fillcircle(p2, thickness / 2, color);
02061             }
02062             fillrect(p1.x-thickness/2,p1.y, p2.x+thickness/2,p2.y, color);
02063         } else if (p1.y == p2.y) {
02064             // horizontal
02065             if (roundCap) {
02066                 fillcircle(p1, thickness / 2, color);
02067                 fillcircle(p2, thickness / 2, color);
02068             }
02069             fillrect(p1.x,p1.y-thickness/2, p2.x,p2.y+thickness/2, color);
02070         } else {
02071             // some diagonal, drawn rather slowly with filled circles
02072             // @todo draw the end-points with circles, then draw the diagonal
02073             //      with 2 triangles.
02074             //Round-caps
02075             if (roundCap) {
02076                 fillcircle(p1, thickness / 2, color);
02077                 fillcircle(p2, thickness / 2, color);
02078             }
02079             // Compute the perpendicular points to draw the triangles
02080             //              +           fillTriangle: p1a,p1b,p2a
02081             //            /   + p1a                   p1a,p2a,p2b
02082             //           +  +p1+ .  .   .   .    .
02083             //       p1b  +   /  .      angle
02084             //              +        .
02085             //                           .
02086             //
02087             //                                   .        +
02088             //                                          /   + p2a
02089             //                                         +  +p2+
02090             //                                     p2b  +   /
02091             //                                            +
02092             point_t pTri[4];
02093             float slope = (p2.y - p1.y) / (p2.x - p1.x);
02094             slope = -1/slope;
02095             //centerline
02096             //line(p1,p2,color);
02097             float dx = (thickness/2 / sqrt(thickness/2 + (slope * slope)));
02098             float dy = slope * dx;
02099             pTri[0].x = p1.x + dx;
02100             pTri[0].y = p1.y + dy;
02101             pTri[1].x = p1.x - dx;
02102             pTri[1].y = p1.y - dy;
02103             pTri[2].x = p2.x + dx;
02104             pTri[2].y = p2.y + dy;
02105             pTri[3].x = p2.x - dx;
02106             pTri[3].y = p2.y - dy;
02107             filltriangle(pTri[0],pTri[1],pTri[3], color);
02108             filltriangle(pTri[0],pTri[2],pTri[3], color);
02109         }
02110     }
02111     return noerror;
02112 }
02113 
02114 
02115 bool RA8875::SetEndCap(bool _roundCap) {
02116     bool prevCap = roundCap;
02117     roundCap = _roundCap;
02118     return prevCap;
02119 }
02120 
02121 
02122 //
02123 // Rectangle functions all mostly helpers to the basic rectangle function
02124 //
02125 
02126 RetCode_t RA8875::fillrect(rect_t r, color_t color, fill_t fillit)
02127 {
02128     return rect(r.p1.x, r.p1.y, r.p2.x, r.p2.y, color, fillit);
02129 }
02130 
02131 RetCode_t RA8875::fillrect(loc_t x1, loc_t y1, loc_t x2, loc_t y2,
02132                            color_t color, fill_t fillit)
02133 {
02134     return rect(x1,y1,x2,y2,color,fillit);
02135 }
02136 
02137 RetCode_t RA8875::rect(rect_t r, color_t color, fill_t fillit)
02138 {
02139     return rect(r.p1.x, r.p1.y, r.p2.x, r.p2.y, color, fillit);
02140 }
02141 
02142 RetCode_t RA8875::rect(loc_t x1, loc_t y1, loc_t x2, loc_t y2,
02143                        color_t color, fill_t fillit)
02144 {
02145     foreground(color);
02146     return rect(x1,y1,x2,y2,fillit);
02147 }
02148 
02149 RetCode_t RA8875::rect(loc_t x1, loc_t y1, loc_t x2, loc_t y2,
02150                        fill_t fillit)
02151 {
02152     RetCode_t ret = noerror;
02153     PERFORMANCE_RESET;
02154     // check for bad_parameter
02155     if (x1 < 0 || x1 >= virt_screenwidth || x2 < 0 || x2 >= virt_screenwidth
02156     || y1 < 0 || y1 >= virt_screenheight || y2 < 0 || y2 >= virt_screenheight) {
02157         ret = bad_parameter;
02158     } else {
02159         if (x1 == x2 && y1 == y2) {
02160             pixel(x1, y1);
02161         } else if (x1 == x2) {
02162             line(x1, y1, x2, y2);
02163         } else if (y1 == y2) {
02164             line(x1, y1, x2, y2);
02165         } else {
02166             switch (screen_orientation) {
02167             case rotate_0:
02168             case rotate_180:
02169                 WriteCommandW(RA8875_DLHSR0, x1);
02170                 WriteCommandW(RA8875_DLVSR0, y1);
02171                 WriteCommandW(RA8875_DLHER0, x2);
02172                 WriteCommandW(RA8875_DLVER0, y2);
02173                 break;
02174             case rotate_90:
02175             case rotate_270:
02176                 WriteCommandW(RA8875_DLHSR0, y1);
02177                 WriteCommandW(RA8875_DLVSR0, x1);
02178                 WriteCommandW(RA8875_DLHER0, y2);
02179                 WriteCommandW(RA8875_DLVER0, x2);
02180                 break;
02181             case invalid:
02182                 REGISTERPERFORMANCE(PRF_DRAWRECTANGLE);
02183                 ret = bad_parameter;
02184                 return ret;
02185                 //break;
02186             }
02187             unsigned char drawCmd = 0x10;   // Rectangle
02188             if (fillit == FILL)
02189                 drawCmd |= 0x20;
02190             WriteCommand(RA8875_DCR, drawCmd);
02191             ret = WriteCommand(RA8875_DCR, 0x80 + drawCmd); // Start drawing.
02192             if (!_WaitWhileReg(0x90, 0x80)) {
02193                 REGISTERPERFORMANCE(PRF_DRAWRECTANGLE);
02194                 return external_abort;
02195             }
02196         }
02197     }
02198     REGISTERPERFORMANCE(PRF_DRAWRECTANGLE);
02199     return ret;
02200 }
02201 
02202 
02203 //
02204 // rounded rectangle functions are mostly helpers to the base round rect
02205 //
02206 
02207 RetCode_t RA8875::fillroundrect(rect_t r, dim_t radius1, dim_t radius2, color_t color, fill_t fillit)
02208 {
02209     return roundrect(r.p1.x, r.p1.y, r.p2.x, r.p2.y, radius1, radius2, color, fillit);
02210 }
02211 
02212 RetCode_t RA8875::fillroundrect(loc_t x1, loc_t y1, loc_t x2, loc_t y2,
02213                                 dim_t radius1, dim_t radius2, color_t color, fill_t fillit)
02214 {
02215     foreground(color);
02216     return roundrect(x1,y1,x2,y2,radius1,radius2,fillit);
02217 }
02218 
02219 RetCode_t RA8875::roundrect(rect_t r, dim_t radius1, dim_t radius2, color_t color, fill_t fillit)
02220 {
02221     return roundrect(r.p1.x, r.p1.y, r.p2.x, r.p2.y, radius1, radius2, color, fillit);
02222 }
02223 
02224 RetCode_t RA8875::roundrect(loc_t x1, loc_t y1, loc_t x2, loc_t y2,
02225                             dim_t radius1, dim_t radius2, color_t color, fill_t fillit)
02226 {
02227     foreground(color);
02228     return roundrect(x1,y1,x2,y2,radius1,radius2,fillit);
02229 }
02230 
02231 
02232 RetCode_t RA8875::roundrect(loc_t x1, loc_t y1, loc_t x2, loc_t y2,
02233                             dim_t radius1, dim_t radius2, fill_t fillit)
02234 {
02235     RetCode_t ret = noerror;
02236 
02237     INFO("roundrect()");
02238     PERFORMANCE_RESET;
02239     if (x1 < 0 || x1 >= virt_screenwidth || x2 < 0 || x2 >= virt_screenwidth
02240     || y1 < 0 || y1 >= virt_screenheight || y2 < 0 || y2 >= virt_screenheight) {
02241         ret = bad_parameter;
02242     } else if (x1 > x2 || y1 > y2 || (radius1 > (x2-x1)/2) || (radius2 > (y2-y1)/2) ) {
02243         ret = bad_parameter;
02244     } else if (x1 == x2 && y1 == y2) {
02245         pixel(x1, y1);
02246     } else if (x1 == x2) {
02247         line(x1, y1, x2, y2);
02248     } else if (y1 == y2) {
02249         line(x1, y1, x2, y2);
02250     } else {
02251         switch (screen_orientation) {
02252         case rotate_0:
02253         case rotate_180:
02254             WriteCommandW(RA8875_DLHSR0, x1);
02255             WriteCommandW(RA8875_DLVSR0, y1);
02256             WriteCommandW(RA8875_DLHER0, x2);
02257             WriteCommandW(RA8875_DLVER0, y2);
02258             WriteCommandW(RA8875_ELLA0, radius1);
02259             WriteCommandW(RA8875_ELLB0, radius2);
02260             break;
02261         case rotate_90:
02262         case rotate_270:
02263             WriteCommandW(RA8875_DLHSR0, y1);
02264             WriteCommandW(RA8875_DLVSR0, x1);
02265             WriteCommandW(RA8875_DLHER0, y2);
02266             WriteCommandW(RA8875_DLVER0, x2);
02267             WriteCommandW(RA8875_ELLA0, radius2);
02268             WriteCommandW(RA8875_ELLB0, radius1);
02269             break;
02270         case invalid:
02271             REGISTERPERFORMANCE(PRF_DRAWROUNDEDRECTANGLE);
02272             return bad_parameter;
02273             //break;
02274         }
02275         // Should not need this...
02276         WriteCommandW(RA8875_DEHR0, 0);
02277         WriteCommandW(RA8875_DEVR0, 0);
02278         unsigned char drawCmd = 0x20;       // Rounded Rectangle
02279         if (fillit == FILL)
02280             drawCmd |= 0x40;
02281         WriteCommand(RA8875_ELLIPSE, drawCmd);
02282         WriteCommand(RA8875_ELLIPSE, 0x80 + drawCmd); // Start drawing.
02283         if (!_WaitWhileReg(0xA0, 0x80)) {
02284             REGISTERPERFORMANCE(PRF_DRAWROUNDEDRECTANGLE);
02285             return external_abort;
02286         }
02287     }
02288     REGISTERPERFORMANCE(PRF_DRAWROUNDEDRECTANGLE);
02289     return ret;
02290 }
02291 
02292 
02293 //
02294 // triangle functions
02295 //
02296 RetCode_t RA8875::filltriangle(point_t p1, point_t p2, point_t p3, color_t color, fill_t fillit)
02297 {
02298     return filltriangle(p1.x,p1.y, p2.x,p2.y, p3.x,p3.y, color, fillit);
02299 }
02300 
02301 RetCode_t RA8875::triangle(point_t p1, point_t p2, point_t p3, color_t color, fill_t fillit)
02302 {
02303     return triangle(p1.x,p1.y, p2.x,p2.y, p3.x,p3.y, color, fillit);
02304 }
02305 
02306 RetCode_t RA8875::triangle(loc_t x1, loc_t y1, loc_t x2, loc_t y2,
02307                            loc_t x3, loc_t y3, color_t color, fill_t fillit)
02308 {
02309     RetCode_t ret;
02310 
02311     if (x1 < 0 || x1 >= virt_screenwidth || x2 < 0 || x2 >= virt_screenwidth || x3 < 0 || x3 >= virt_screenwidth
02312     || y1 < 0 || y1 >= virt_screenheight || y2 < 0 || y2 >= virt_screenheight || y3 < 0 || y3 >= virt_screenheight)
02313         ret = bad_parameter;
02314     foreground(color);
02315     ret = triangle(x1,y1,x2,y2,x3,y3,fillit);
02316     return ret;
02317 }
02318 
02319 
02320 RetCode_t RA8875::filltriangle(loc_t x1, loc_t y1, loc_t x2, loc_t y2,
02321                                loc_t x3, loc_t y3, color_t color, fill_t fillit)
02322 {
02323     RetCode_t ret;
02324 
02325     foreground(color);
02326     ret = triangle(x1,y1,x2,y2,x3,y3,fillit);
02327     return ret;
02328 }
02329 
02330 
02331 RetCode_t RA8875::triangle(loc_t x1, loc_t y1 ,loc_t x2, loc_t y2,
02332                            loc_t x3, loc_t y3, fill_t fillit)
02333 {
02334     RetCode_t ret = noerror;
02335 
02336     INFO("triangle");
02337     PERFORMANCE_RESET;
02338     if (x1 == x2 && y1 == y2 && x1 == x3 && y1 == y3) {
02339         pixel(x1, y1);
02340     } else {
02341         switch (screen_orientation) {
02342         case rotate_0:
02343         case rotate_180:
02344             WriteCommandW(RA8875_DLHSR0, x1);
02345             WriteCommandW(RA8875_DLVSR0, y1);
02346             WriteCommandW(RA8875_DLHER0, x2);
02347             WriteCommandW(RA8875_DLVER0, y2);
02348             WriteCommandW(RA8875_DTPH0, x3);
02349             WriteCommandW(RA8875_DTPV0, y3);
02350             break;
02351         case rotate_90:
02352         case rotate_270:
02353             WriteCommandW(RA8875_DLHSR0, y1);
02354             WriteCommandW(RA8875_DLVSR0, x1);
02355             WriteCommandW(RA8875_DLHER0, y2);
02356             WriteCommandW(RA8875_DLVER0, x2);
02357             WriteCommandW(RA8875_DTPH0, y3);
02358             WriteCommandW(RA8875_DTPV0, x3);
02359             break;
02360         case invalid:
02361             REGISTERPERFORMANCE(PRF_DRAWTRIANGLE);
02362             return bad_parameter;
02363             //break;
02364         }
02365         unsigned char drawCmd = 0x01;       // Triangle
02366         if (fillit == FILL)
02367             drawCmd |= 0x20;
02368         WriteCommand(RA8875_DCR, drawCmd);
02369         WriteCommand(RA8875_DCR, 0x80 + drawCmd); // Start drawing.
02370         if (!_WaitWhileReg(0x90, 0x80)) {
02371             REGISTERPERFORMANCE(PRF_DRAWTRIANGLE);
02372             return external_abort;
02373         }
02374     }
02375     REGISTERPERFORMANCE(PRF_DRAWTRIANGLE);
02376     return ret;
02377 }
02378 
02379 
02380 RetCode_t RA8875::circle(point_t p, dim_t radius,
02381                          color_t color, fill_t fillit)
02382 {
02383     foreground(color);
02384     return circle(p.x,p.y,radius,fillit);
02385 }
02386 
02387 
02388 RetCode_t RA8875::fillcircle(point_t p, dim_t radius,
02389                              color_t color, fill_t fillit)
02390 {
02391     foreground(color);
02392     return circle(p.x,p.y,radius,fillit);
02393 }
02394 
02395 
02396 RetCode_t RA8875::circle(point_t p, dim_t radius, fill_t fillit)
02397 {
02398     return circle(p.x,p.y,radius,fillit);
02399 }
02400 
02401 
02402 RetCode_t RA8875::circle(loc_t x, loc_t y, dim_t radius,
02403                          color_t color, fill_t fillit)
02404 {
02405     foreground(color);
02406     return circle(x,y,radius,fillit);
02407 }
02408 
02409 
02410 RetCode_t RA8875::fillcircle(loc_t x, loc_t y, dim_t radius,
02411                              color_t color, fill_t fillit)
02412 {
02413     foreground(color);
02414     return circle(x,y,radius,fillit);
02415 }
02416 
02417 RetCode_t RA8875::circle(loc_t x, loc_t y, dim_t radius, fill_t fillit)
02418 {
02419     RetCode_t ret = noerror;
02420 
02421     INFO("circle");
02422     PERFORMANCE_RESET;
02423     if ((x - radius) < 0 || (x + radius) > width()
02424     || (y - radius) < 0 || (y + radius) > height()) {
02425         ret = bad_parameter;
02426     } else if (radius == 1) {
02427         pixel(x,y);
02428     } else {
02429         switch (screen_orientation) {
02430         case rotate_0:
02431         case rotate_180:
02432             WriteCommandW(RA8875_DCHR0, x);
02433             WriteCommandW(RA8875_DCVR0, y);
02434             break;
02435         case rotate_90:
02436         case rotate_270:
02437             WriteCommandW(RA8875_DCHR0, y);
02438             WriteCommandW(RA8875_DCVR0, x);
02439             break;
02440         case invalid:
02441             REGISTERPERFORMANCE(PRF_DRAWCIRCLE);
02442             return bad_parameter;
02443             //break;
02444         }
02445         WriteCommand(RA8875_DCRR, radius & 0xFF);
02446         unsigned char drawCmd = 0x00;       // Circle
02447         if (fillit == FILL)
02448             drawCmd |= 0x20;
02449         WriteCommand(RA8875_DCR, drawCmd);
02450         WriteCommand(RA8875_DCR, 0x40 + drawCmd); // Start drawing.
02451         if (!_WaitWhileReg(0x90, 0x40)) {
02452             REGISTERPERFORMANCE(PRF_DRAWCIRCLE);
02453             return external_abort;
02454         }
02455     }
02456     REGISTERPERFORMANCE(PRF_DRAWCIRCLE);
02457     return ret;
02458 }
02459 
02460 
02461 RetCode_t RA8875::ellipse(point_t p, dim_t radius1, dim_t radius2, color_t color, fill_t fillit)
02462 {
02463     return ellipse(p.x, p.y, radius1, radius2, color, fillit);
02464 }
02465 
02466 
02467 RetCode_t RA8875::fillellipse(point_t p, dim_t radius1, dim_t radius2, color_t color, fill_t fillit)
02468 {
02469     return fillellipse(p.x, p.y, radius1, radius2, color, fillit);
02470 }
02471 
02472 
02473 RetCode_t RA8875::ellipse(loc_t x, loc_t y, dim_t radius1, dim_t radius2, color_t color, fill_t fillit)
02474 {
02475     foreground(color);
02476     return ellipse(x,y,radius1,radius2,fillit);
02477 }
02478 
02479 
02480 RetCode_t RA8875::fillellipse(loc_t x, loc_t y, dim_t radius1, dim_t radius2, color_t color, fill_t fillit)
02481 {
02482     foreground(color);
02483     return ellipse(x,y,radius1,radius2,fillit);
02484 }
02485 
02486 
02487 RetCode_t RA8875::ellipse(loc_t x, loc_t y, dim_t radius1, dim_t radius2, fill_t fillit)
02488 {
02489     RetCode_t ret = noerror;
02490 
02491     INFO("ellipse");
02492     PERFORMANCE_RESET;
02493     if ((x - radius1) < 0 || (x + radius1) > virt_screenwidth
02494     || (y - radius2) < 0 || (y + radius2) > virt_screenheight) {
02495         ret = bad_parameter;
02496     } else if (radius1 == 1 && radius2 == 1) {
02497         pixel(x, y);
02498     } else {
02499         switch (screen_orientation) {
02500         case rotate_0:
02501         case rotate_180:
02502             WriteCommandW(RA8875_DEHR0, x);
02503             WriteCommandW(RA8875_DEVR0, y);
02504             break;
02505         case rotate_90:
02506         case rotate_270:
02507             WriteCommandW(RA8875_DEHR0, y);
02508             WriteCommandW(RA8875_DEVR0, x);
02509             break;
02510         case invalid:
02511             REGISTERPERFORMANCE(PRF_DRAWELLIPSE);
02512             return bad_parameter;
02513             //break;
02514         }
02515         WriteCommandW(RA8875_ELLA0, radius1);
02516         WriteCommandW(RA8875_ELLB0, radius2);
02517         unsigned char drawCmd = 0x00;   // Ellipse
02518         if (fillit == FILL)
02519             drawCmd |= 0x40;
02520         WriteCommand(RA8875_ELLIPSE, drawCmd);
02521         WriteCommand(RA8875_ELLIPSE, 0x80 + drawCmd); // Start drawing.
02522         if (!_WaitWhileReg(0xA0, 0x80)) {
02523             REGISTERPERFORMANCE(PRF_DRAWELLIPSE);
02524             return external_abort;
02525         }
02526     }
02527     REGISTERPERFORMANCE(PRF_DRAWELLIPSE);
02528     return ret;
02529 }
02530 
02531 
02532 RetCode_t RA8875::frequency(unsigned long Hz, unsigned long Hz2)
02533 {
02534     spiwritefreq = Hz;
02535     if (Hz2 != 0)
02536         spireadfreq = Hz2;
02537     else
02538         spireadfreq = Hz/2;
02539     _setWriteSpeed(true);
02540     //       __   ___
02541     // Clock   ___A     Rising edge latched
02542     //       ___ ____
02543     // Data  ___X____
02544     spi.format(8, 3);           // 8 bits and clock to data phase 0
02545     return noerror;
02546 }
02547 
02548 void RA8875::_setWriteSpeed(bool writeSpeed)
02549 {
02550     if (writeSpeed) {
02551         spi.frequency(spiwritefreq);
02552         spiWriteSpeed = true;
02553     } else {
02554         spi.frequency(spireadfreq);
02555         spiWriteSpeed = false;
02556     }
02557 }
02558 
02559 
02560 
02561 RetCode_t RA8875::BlockMove(uint8_t dstLayer, uint8_t dstDataSelect, point_t dstPoint,
02562     uint8_t srcLayer, uint8_t srcDataSelect, point_t srcPoint,
02563     dim_t bte_width, dim_t bte_height,
02564     uint8_t bte_op_code, uint8_t bte_rop_code)
02565 {
02566     uint8_t cmd;
02567 
02568     PERFORMANCE_RESET;
02569     ///@todo range check and error return rather than to secretly fix
02570     srcPoint.x &= 0x3FF;    // prevent high bits from doing unexpected things
02571     srcPoint.y &= 0x1FF;
02572     dstPoint.x &= 0x3FF;
02573     dstPoint.y &= 0x1FF;
02574     WriteCommandW(RA8875_HSBE0, srcPoint.x);
02575     WriteCommandW(RA8875_VSBE0, ((dim_t)(srcLayer & 1) << 15) | srcPoint.y);
02576     WriteCommandW(RA8875_HDBE0, dstPoint.x);
02577     WriteCommandW(RA8875_VDBE0, ((dim_t)(dstLayer & 1) << 15) | dstPoint.y);
02578     WriteCommandW(RA8875_BEWR0, bte_width);
02579     WriteCommandW(RA8875_BEHR0, bte_height);
02580     WriteCommand(RA8875_BECR1,  ((bte_rop_code & 0x0F) << 4) | (bte_op_code & 0x0F));
02581     cmd = ((srcDataSelect & 1) << 6) | ((dstDataSelect & 1) << 5);
02582     WriteCommand(RA8875_BECR0, 0x80 | cmd);     // enable the BTE
02583     if (!_WaitWhileBusy(0x40)) {
02584         REGISTERPERFORMANCE(PRF_BLOCKMOVE);
02585         return external_abort;
02586     }
02587     REGISTERPERFORMANCE(PRF_BLOCKMOVE);
02588     return noerror;
02589 }
02590 
02591 
02592 RetCode_t RA8875::Power(bool on)
02593 {
02594     WriteCommand(RA8875_PWRR, (on) ? 0x80 : 0x00);
02595     return noerror;
02596 }
02597 
02598 
02599 RetCode_t RA8875::Backlight_u8(uint8_t brightness)
02600 {
02601     static bool is_enabled = false;
02602 
02603     if (brightness == 0) {
02604         WriteCommand(RA8875_P1CR); // Disable the PWM
02605         WriteData(0x00);
02606         is_enabled = false;
02607     } else if (!is_enabled) {
02608         WriteCommand(RA8875_P1CR); // Enable the PWM
02609         WriteData(0x80);
02610         WriteCommand(RA8875_P1CR); // Not sure why this is needed, but following the pattern
02611         WriteData(0x81);    // open PWM (SYS_CLK / 2 as best I can tell)
02612         is_enabled = true;
02613     }
02614     WriteCommand(RA8875_P1DCR, brightness);  // Brightness parameter 0xff-0x00
02615     return noerror;
02616 }
02617 
02618 uint8_t RA8875::GetBacklight_u8(void)
02619 {
02620     return ReadCommand(0x8b);
02621 }
02622 
02623 RetCode_t RA8875::Backlight(float brightness)
02624 {
02625     unsigned char b;
02626 
02627     if (brightness >= 1.0)
02628         b = 255;
02629     else if (brightness <= 0.0)
02630         b = 0;
02631     else
02632         b = (unsigned char)(brightness * 255);
02633     return Backlight_u8(b);
02634 }
02635 
02636 float RA8875::GetBacklight(void)
02637 {
02638     return (float)(GetBacklight_u8())/255;
02639 }
02640 
02641 RetCode_t RA8875::SelectUserFont(const uint8_t * _font)
02642 {
02643     INFO("SelectUserFont(%p)", _font);
02644     if (_font) {
02645         HexDump("Font Memory", _font, 16);
02646         extFontHeight = _font[6];
02647         uint32_t totalWidth = 0;
02648         uint16_t firstChar = _font[3] * 256 + _font[2];
02649         uint16_t lastChar  = _font[5] * 256 + _font[4];
02650         uint16_t i;
02651 
02652         for (i=firstChar; i<=lastChar; i++) {
02653             // 8 bytes of preamble to the first level lookup table
02654             uint16_t offsetToCharLookup = 8 + 4 * (i - firstChar);    // 4-bytes: width(pixels), 16-bit offset from table start, 0
02655             totalWidth += _font[offsetToCharLookup];
02656         }
02657         extFontWidth = totalWidth / (lastChar - firstChar);
02658         INFO("Font Metrics: Avg W: %2d, H: %2d, First:%d, Last:%d", extFontWidth, extFontHeight, firstChar, lastChar);
02659     }
02660     SetTextCursor(GetTextCursor());  // soft-font cursor -> hw cursor
02661     font = _font;
02662     return GraphicsDisplay::SelectUserFont(_font);
02663 }
02664 
02665 color_t RA8875::background(color_t color)
02666 {
02667     color_t oldColor = GetBackColor();
02668     GraphicsDisplay::background(color);
02669     _writeColorTrio(0x60, color);
02670     return oldColor;
02671 }
02672 
02673 
02674 color_t RA8875::background(unsigned char r, unsigned char g, unsigned char b)
02675 {
02676     return background(RGB(r,g,b));
02677 }
02678 
02679 color_t RA8875::GetBackColor(void)
02680 {
02681     return _readColorTrio(0x60);
02682 }
02683 
02684 
02685 color_t RA8875::foreground(color_t color)
02686 {
02687     color_t oldColor = GetForeColor();
02688     GraphicsDisplay::foreground(color);
02689     _writeColorTrio(0x63, color);
02690     return oldColor;
02691 }
02692 
02693 
02694 color_t RA8875::foreground(unsigned char r, unsigned char g, unsigned char b)
02695 {
02696     return foreground(RGB(r,g,b));
02697 }
02698 
02699 color_t RA8875::GetForeColor(void)
02700 {
02701     return _readColorTrio(0x63);
02702 }
02703 
02704 
02705 
02706 color_t RA8875::DOSColor(int i)
02707 {
02708     const color_t colors[16] = {
02709         Black,    Blue,       Green,       Cyan,
02710         Red,      Magenta,    Brown,       Gray,
02711         Charcoal, BrightBlue, BrightGreen, BrightCyan,
02712         Orange,   Pink,       Yellow,      White
02713     };
02714     if (i >= 0 && i < 16)
02715         return colors[i];
02716     else
02717         return 0;
02718 }
02719 
02720 
02721 const char * RA8875::DOSColorNames(int i)
02722 {
02723     const char * names[16] = {
02724         "Black",    "Blue",       "Green",       "Cyan",
02725         "Red",      "Magenta",    "Brown",       "Gray",
02726         "Charcoal", "BrightBlue", "BrightGreen", "BrightCyan",
02727         "Orange",   "Pink",       "Yellow",      "White"
02728     };
02729     if (i >= 0 && i < 16)
02730         return names[i];
02731     else
02732         return NULL;
02733 }
02734 
02735 
02736 ///////////////////////////////////////////////////////////////
02737 // Private functions
02738 
02739 unsigned char RA8875::_spiwrite(unsigned char data)
02740 {
02741     unsigned char retval;
02742 
02743     if (!spiWriteSpeed)
02744         _setWriteSpeed(true);
02745     retval = spi.write(data);
02746     return retval;
02747 }
02748 
02749 
02750 unsigned char RA8875::_spiread(void)
02751 {
02752     unsigned char retval;
02753     unsigned char data = 0;
02754 
02755     if (spiWriteSpeed)
02756         _setWriteSpeed(false);
02757     retval = spi.write(data);
02758     return retval;
02759 }
02760 
02761 
02762 RetCode_t RA8875::_select(bool chipsel)
02763 {
02764     cs = (chipsel == true) ? 0 : 1;
02765     return noerror;
02766 }
02767 
02768 
02769 RetCode_t RA8875::PrintScreen(uint16_t layer, loc_t x, loc_t y, dim_t w, dim_t h, const char *Name_BMP)
02770 {
02771     (void)layer;
02772 
02773     // AttachPrintHandler(this, RA8875::_printCallback);
02774     // return PrintScreen(x,y,w,h);
02775     return PrintScreen(x, y, w, h, Name_BMP);
02776 }
02777 
02778 RetCode_t RA8875::_printCallback(RA8875::filecmd_t cmd, uint8_t * buffer, uint16_t size)
02779 {
02780     HexDump("CB", buffer, size);
02781     switch(cmd) {
02782         case RA8875::filecmd_t::OPEN:
02783             //pc.printf("About to write %lu bytes\r\n", *(uint32_t *)buffer);
02784             _printFH = fopen("file.bmp", "w+b");
02785             if (_printFH == 0)
02786                 return file_not_found;
02787             break;
02788         case RA8875::filecmd_t::WRITE:
02789             //pc.printf("  Write %4u bytes\r\n", size);
02790             fwrite(buffer, 1, size, _printFH);
02791             break;
02792         case RA8875::filecmd_t::CLOSE:
02793             //pc.printf("  close\r\n");
02794             fclose(_printFH);
02795             _printFH = 0;
02796             break;
02797         default:
02798             //pc.printf("Unexpected callback %d\r\n", cmd);
02799             return file_not_found;
02800             //break;
02801     }
02802     return noerror;
02803 }
02804 
02805 int RA8875::RoundUp(int value, int roundTo)
02806 {
02807     if (roundTo == 0)
02808         return 0;
02809     return ((value + roundTo - 1) / roundTo) * roundTo;
02810 }
02811 
02812 RetCode_t RA8875::PrintScreen(loc_t x, loc_t y, dim_t w, dim_t h, uint8_t bitsPerPixel)
02813 {
02814     BITMAPFILEHEADER BMP_Header;
02815     BITMAPINFOHEADER BMP_Info;
02816     uint8_t * lineBuffer = NULL;
02817     color_t * pixelBuffer = NULL;
02818     color_t * pixelBuffer2 = NULL;
02819 
02820     INFO("(%d,%d)-(%d,%d)x%d", x,y,w,h,bitsPerPixel);
02821     if (x >= 0 && x < virt_screenwidth
02822             && y >= 0 && y < virt_screenheight
02823             && w > 0 && x + w <= virt_screenwidth
02824             && h > 0 && y + h <= virt_screenheight) {
02825         BMP_Header.bfType = BF_TYPE;
02826         BMP_Header.bfReserved1 = 0;
02827         BMP_Header.bfReserved2 = 0;
02828         switch (bitsPerPixel) {
02829             case 24:
02830                 BMP_Header.bfOffBits = sizeof(BMP_Header) + sizeof(BMP_Info);
02831                 BMP_Header.bfSize = (h * RoundUp(w * sizeof(RGBQUAD),4)) + BMP_Header.bfOffBits;
02832                 break;
02833             case 8:
02834             default:
02835                 BMP_Header.bfOffBits = sizeof(BMP_Header) + sizeof(BMP_Info) + sizeof(WebColorPalette);
02836                 INFO("Initial Offset to Bitstream %lX", BMP_Header.bfOffBits);
02837                 //if (BMP_Header.bfOffBits & 0x03) {
02838                 //    BMP_Header.bfOffBits += (4 - (BMP_Header.bfOffBits & 0x03));
02839                 //}
02840                 BMP_Header.bfSize = (h * RoundUp(w * 1,4)) + BMP_Header.bfOffBits;
02841                 break;
02842         }
02843         INFO("Offset to Bitstream %lX", BMP_Header.bfOffBits);
02844 
02845         // Bytes in the line buffer
02846         int lineBufSize = RoundUp(((bitsPerPixel == 24) ? 3 : 1) * w, 4);
02847         INFO("LineBufSize: %d", lineBufSize);
02848 
02849         BMP_Info.biSize = sizeof(BMP_Info);
02850         BMP_Info.biWidth = w;
02851         BMP_Info.biHeight = h;
02852         BMP_Info.biPlanes = 1;
02853         BMP_Info.biBitCount = bitsPerPixel;
02854         BMP_Info.biCompression = BI_RGB;
02855         BMP_Info.biSizeImage = lineBufSize * h;
02856         BMP_Info.biXPelsPerMeter = 0;
02857         BMP_Info.biYPelsPerMeter = 0;
02858         // for 24-bit, there is no palette, so these are zero
02859         // for 8-bit, there can be up to 256 RGB values in the palette
02860 
02861         BMP_Info.biClrUsed = (bitsPerPixel == 24) ? 0 : sizeof(WebColorPalette)/sizeof(WebColorPalette[0]);    // for 8b/pixel
02862         BMP_Info.biClrImportant = BMP_Info.biClrUsed;
02863 
02864         // Allocate the memory we need to proceed
02865         lineBuffer = (uint8_t *)swMalloc(lineBufSize);
02866         if (lineBuffer == NULL) {
02867             ERR("Not enough RAM for PrintScreen lineBuffer");
02868             return(not_enough_ram);
02869         }
02870         memset(lineBuffer, 0, lineBufSize); // zero-Fill
02871 
02872         #define DOUBLEBUF /* one larger buffer instead of two */
02873 
02874         #ifdef DOUBLEBUF
02875         // In the "#else", pixelBuffer2 malloc returns a value,
02876         // but is actually causing a failure later.
02877         // This test helps determine if it is truly out of memory,
02878         // or if malloc is broken.
02879         pixelBuffer = (color_t *)swMalloc(2 * w * sizeof(color_t));
02880         if (pixelBuffer)
02881             pixelBuffer2 = pixelBuffer + (w * sizeof(color_t));
02882         else
02883             pixelBuffer2 = NULL;
02884         #else
02885         pixelBuffer = (color_t *)swMalloc(w * sizeof(color_t));
02886         pixelBuffer2 = (color_t *)swMalloc(w * sizeof(color_t));
02887         #endif
02888         if (pixelBuffer == NULL || pixelBuffer2 == NULL) {
02889             ERR("Not enough RAM for pixelBuffer");
02890             #ifndef DOUBLEBUF
02891             if (pixelBuffer2)
02892                 swFree(pixelBuffer2);
02893             #endif
02894             if (pixelBuffer)
02895                 swFree(pixelBuffer);
02896             swFree(lineBuffer);
02897             return(not_enough_ram);
02898         }
02899 
02900         // Get the file primed...
02901         /// @todo check return value for possibility of a fatal error
02902         privateCallback(filecmd_t::OPEN, (uint8_t *)&BMP_Header.bfSize, 4);
02903 
02904         // Be optimistic - don't check for errors.
02905         //HexDump("BMP_Header", (uint8_t *)&BMP_Header, sizeof(BMP_Header));
02906         //fwrite(&BMP_Header, sizeof(char), sizeof(BMP_Header), Image);
02907         privateCallback(filecmd_t::WRITE, (uint8_t *)&BMP_Header, sizeof(BMP_Header));
02908 
02909         //HexDump("BMP_Info", (uint8_t *)&BMP_Info, sizeof(BMP_Info));
02910         //fwrite(&BMP_Info, sizeof(char), sizeof(BMP_Info), Image);
02911         privateCallback(filecmd_t::WRITE, (uint8_t *)&BMP_Info, sizeof(BMP_Info));
02912         if (bitsPerPixel != 24) {
02913             //HexDump("Palette", (uint8_t *)&WebColorPalette, sizeof(WebColorPalette));
02914             //fwrite(&WebColorPalette, sizeof(char), sizeof(WebColorPalette), Image);
02915             privateCallback(filecmd_t::WRITE, (uint8_t *)&WebColorPalette, sizeof(WebColorPalette));
02916             //if (sizeof(WebColorPalette) % 4) {
02917             //    const uint8_t padd[] = { 0, 0, 0 };
02918             //    //fwrite(&padd, sizeof(char), (sizeof(BMP_Header) + sizeof(BMP_Info) + sizeof(WebColorPalette)) % 4, Image);
02919             //    privateCallback(filecmd_t::WRITE, (uint8_t *)&padd, (sizeof(BMP_Header) + sizeof(BMP_Info) + sizeof(WebColorPalette)) % 4);
02920             //}
02921         }
02922         //color_t transparency = GetBackgroundTransparencyColor();
02923         LayerMode_T ltpr0 = GetLayerMode();
02924         uint16_t prevLayer = GetDrawingLayer();
02925         // If only one of the layers is visible, select that layer
02926         switch(ltpr0) {
02927             case ShowLayer0:
02928                 SelectDrawingLayer(0);
02929                 break;
02930             case ShowLayer1:
02931                 SelectDrawingLayer(1);
02932                 break;
02933             default:
02934                 break;
02935         }
02936 
02937         // Read the display from the last line toward the top
02938         // so we can write the file in one pass.
02939         for (int j = h - 1; j >= 0; j--) {
02940             if (idle_callback && h >= 1) {
02941                 (*idle_callback)(progress, (h - 1 - j) * 100 / (h - 1));
02942             }
02943             if (ltpr0 >= 2)             // Need to combine the layers...
02944                 SelectDrawingLayer(0);  // so read layer 0 first
02945             // Read one line of pixels to a local buffer
02946             INFO("x,y %d,%d", x, y + j);
02947             if (getPixelStream(pixelBuffer, w, x,y+j) != noerror) {
02948                 ERR("getPixelStream error, and no recovery handler...");
02949             }
02950             if (ltpr0 >= 2) {           // Need to combine the layers...
02951                 SelectDrawingLayer(1);  // so read layer 1 next
02952                 if (getPixelStream(pixelBuffer2, w, x,y+j) != noerror) {
02953                     ERR("getPixelStream error, and no recovery handler...");
02954                 }
02955             }
02956             INFO("Line: %3d", j);
02957             //HexDump("Raster", (uint8_t *)pixelBuffer, w * sizeof(color_t));
02958             // Convert the local buffer to RGBQUAD format
02959             int lb = 0;
02960             for (int i=0; i<w; i++) {
02961                 color_t tColor = pixelBuffer[x+i];
02962                 tColor = (tColor >> 8) | (tColor << 8);     // Byte Swap
02963                 RGBQUAD q0 = RGB16ToRGBQuad(tColor);        // Scale to 24-bits
02964                 tColor = pixelBuffer2[x+i];
02965                 tColor = (tColor >> 8) | (tColor << 8);     // Byte Swap
02966                 RGBQUAD q1 = RGB16ToRGBQuad(tColor);        // Scale to 24-bits
02967                 switch (ltpr0) {
02968                     case 0:
02969                     case 1:
02970                     case 2: // lighten-overlay  (@TODO Not supported yet)
02971                     case 6: // Floating Windows     (@TODO not sure how to support)
02972                     default: // Reserved...
02973                         //lineBuffer[lb++] = q0.rgbBlue;
02974                         //lineBuffer[lb++] = q0.rgbGreen;
02975                         //lineBuffer[lb++] = q0.rgbRed;
02976                         break;
02977                     case 3: // transparent mode (@TODO Read the background color register for transparent)
02978                     case 4: // boolean or
02979                         q0.rgbBlue = q0.rgbBlue | q1.rgbBlue;
02980                         q0.rgbGreen = q0.rgbGreen | q1.rgbGreen;
02981                         q0.rgbRed = q0.rgbRed | q1.rgbRed;
02982                         break;
02983                     case 5: // boolean AND
02984                         q0.rgbBlue = q0.rgbBlue & q1.rgbBlue;
02985                         q0.rgbGreen = q0.rgbGreen & q1.rgbGreen;
02986                         q0.rgbRed = q0.rgbRed & q1.rgbRed;
02987                         break;
02988                 }
02989                 switch (bitsPerPixel) {
02990                     case 24:
02991                         lineBuffer[lb++] = q0.rgbBlue;
02992                         lineBuffer[lb++] = q0.rgbGreen;
02993                         lineBuffer[lb++] = q0.rgbRed;
02994                         break;
02995                     case 8:
02996                     default:
02997                         lineBuffer[lb++] = FindNearestWebColor(q0.rgbRed,q0.rgbGreen,q0.rgbBlue);
02998                         break;
02999                 }
03000             }
03001             if (j == h - 1) {
03002                 HexDump("Line", lineBuffer, lineBufSize);
03003             }
03004             // Write to disk
03005             privateCallback(WRITE, (uint8_t *)lineBuffer, lineBufSize);
03006         }
03007         SelectDrawingLayer(prevLayer);
03008         privateCallback(CLOSE, NULL, 0);
03009         #ifndef DOUBLEBUF
03010         if (pixelBuffer2)
03011             swFree(pixelBuffer2);
03012         #endif
03013         if (pixelBuffer)
03014             swFree(pixelBuffer);
03015         swFree(lineBuffer);
03016         INFO("Image closed");
03017         return noerror;
03018     } else {
03019         return bad_parameter;
03020     }
03021 }
03022 
03023 
03024 
03025 RetCode_t RA8875::PrintScreen(loc_t x, loc_t y, dim_t w, dim_t h, const char *Name_BMP, uint8_t bitsPerPixel)
03026 {
03027     BITMAPFILEHEADER BMP_Header;
03028     BITMAPINFOHEADER BMP_Info;
03029     uint8_t * lineBuffer = NULL;
03030     color_t * pixelBuffer = NULL;
03031     color_t * pixelBuffer2 = NULL;
03032 
03033     //INFO("(%d,%d)-(%d,%d)x%d %s", x,y,w,h,bitsPerPixel,Name_BMP);
03034     if (x >= 0 && x < virt_screenwidth
03035             && y >= 0 && y < virt_screenheight
03036             && w > 0 && x + w <= virt_screenwidth
03037             && h > 0 && y + h <= virt_screenheight) {
03038         BMP_Header.bfType = BF_TYPE;
03039         BMP_Header.bfReserved1 = 0;
03040         BMP_Header.bfReserved2 = 0;
03041         switch (bitsPerPixel) {
03042             case 24:
03043                 BMP_Header.bfOffBits = sizeof(BMP_Header) + sizeof(BMP_Info);
03044                 BMP_Header.bfSize = (h * RoundUp(w * sizeof(RGBQUAD),4)) + BMP_Header.bfOffBits;
03045                 break;
03046             case 8:
03047             default:
03048                 BMP_Header.bfOffBits = sizeof(BMP_Header) + sizeof(BMP_Info) + sizeof(WebColorPalette);
03049                 //INFO("Initial Offset to Bitstream %lX", BMP_Header.bfOffBits);
03050                 //if (BMP_Header.bfOffBits & 0x03) {
03051                 //    BMP_Header.bfOffBits += (4 - (BMP_Header.bfOffBits & 0x03));
03052                 //}
03053                 BMP_Header.bfSize = (h * RoundUp(w * 1,4)) + BMP_Header.bfOffBits;
03054                 break;
03055         }
03056         //INFO("Offset to Bitstream %lX", BMP_Header.bfOffBits);
03057 
03058         // Bytes in the line buffer
03059         int lineBufSize = RoundUp(((bitsPerPixel == 24) ? 3 : 1) * w, 4);
03060         //INFO("LineBufSize: %d", lineBufSize);
03061 
03062         BMP_Info.biSize = sizeof(BMP_Info);
03063         BMP_Info.biWidth = w;
03064         BMP_Info.biHeight = h;
03065         BMP_Info.biPlanes = 1;
03066         BMP_Info.biBitCount = bitsPerPixel;
03067         BMP_Info.biCompression = BI_RGB;
03068         BMP_Info.biSizeImage = lineBufSize * h;
03069         BMP_Info.biXPelsPerMeter = 0;
03070         BMP_Info.biYPelsPerMeter = 0;
03071         // for 24-bit, there is no palette, so these are zero
03072         // for 8-bit, there can be up to 256 RGB values in the palette
03073 
03074         BMP_Info.biClrUsed = (bitsPerPixel == 24) ? 0 : sizeof(WebColorPalette)/sizeof(WebColorPalette[0]);    // for 8b/pixel
03075         BMP_Info.biClrImportant = BMP_Info.biClrUsed;
03076 
03077         // Allocate the memory we need to proceed
03078         lineBuffer = (uint8_t *)swMalloc(lineBufSize);
03079         if (lineBuffer == NULL) {
03080             ERR("Not enough RAM for PrintScreen lineBuffer");
03081             return(not_enough_ram);
03082         }
03083         memset(lineBuffer, 0, lineBufSize); // zero-Fill
03084 
03085         #define DOUBLEBUF /* one larger buffer instead of two */
03086 
03087         #ifdef DOUBLEBUF
03088         // In the "#else", pixelBuffer2 malloc returns a value,
03089         // but is actually causing a failure later.
03090         // This test helps determine if it is truly out of memory,
03091         // or if malloc is broken.
03092         pixelBuffer = (color_t *)swMalloc(2 * w * sizeof(color_t));
03093         if (pixelBuffer)
03094             pixelBuffer2 = pixelBuffer + (w * sizeof(color_t));
03095         else
03096             pixelBuffer2 = NULL;
03097         #else
03098         pixelBuffer = (color_t *)swMalloc(w * sizeof(color_t));
03099         pixelBuffer2 = (color_t *)swMalloc(w * sizeof(color_t));
03100         #endif
03101         if (pixelBuffer == NULL || pixelBuffer2 == NULL) {
03102             ERR("Not enough RAM for pixelBuffer");
03103             #ifndef DOUBLEBUF
03104             if (pixelBuffer2)
03105                 swFree(pixelBuffer2);
03106             #endif
03107             if (pixelBuffer)
03108                 swFree(pixelBuffer);
03109             swFree(lineBuffer);
03110             return(not_enough_ram);
03111         }
03112 
03113         FILE *Image = fopen(Name_BMP, "wb");
03114         if (!Image) {
03115             ERR("Can't open file for write");
03116             #ifndef DOUBLEBUF
03117             if (pixelBuffer2)
03118                 swFree(pixelBuffer2);
03119             #endif
03120             if (pixelBuffer)
03121                 swFree(pixelBuffer);
03122             swFree(lineBuffer);
03123             return(file_not_found);
03124         }
03125 
03126         // Be optimistic - don't check for errors.
03127         //HexDump("BMP_Header", (uint8_t *)&BMP_Header, sizeof(BMP_Header));
03128         fwrite(&BMP_Header, sizeof(char), sizeof(BMP_Header), Image);
03129 
03130         //HexDump("BMP_Info", (uint8_t *)&BMP_Info, sizeof(BMP_Info));
03131         fwrite(&BMP_Info, sizeof(char), sizeof(BMP_Info), Image);
03132 
03133         if (bitsPerPixel != 24) {
03134             //HexDump("Palette", (uint8_t *)&WebColorPalette, sizeof(WebColorPalette));
03135             fwrite(&WebColorPalette, sizeof(char), sizeof(WebColorPalette), Image);
03136             //if (0 && sizeof(WebColorPalette) % 4) {
03137             //    const uint8_t padd[] = { 0, 0, 0 };
03138             //    fwrite(&padd, sizeof(char),
03139             //        (sizeof(BMP_Header) + sizeof(BMP_Info) + sizeof(WebColorPalette)) % 4, Image);
03140             //}
03141         }
03142         //color_t transparency = GetBackgroundTransparencyColor();
03143         LayerMode_T ltpr0 = GetLayerMode();
03144 
03145         uint16_t prevLayer = GetDrawingLayer();
03146         // If only one of the layers is visible, select that layer
03147         switch(ltpr0) {
03148             case ShowLayer0:
03149                 SelectDrawingLayer(0);
03150                 break;
03151             case ShowLayer1:
03152                 SelectDrawingLayer(1);
03153                 break;
03154             default:
03155                 break;
03156         }
03157 
03158         // Read the display from the last line toward the top
03159         // so we can write the file in one pass.
03160         for (int j = h - 1; j >= 0; j--) {
03161             if (idle_callback && h >= 1) {
03162                 (*idle_callback)(progress, (h - 1 - j) * 100 / (h - 1));
03163             }
03164 
03165             if (ltpr0 >= 2)             // Need to combine the layers...
03166                 SelectDrawingLayer(0);  // so read layer 0 first
03167 
03168             // Read one line of pixels to a local buffer
03169             if (getPixelStream(pixelBuffer, w, x,y+j) != noerror) {
03170                 ERR("getPixelStream error, and no recovery handler...");
03171             }
03172             if (ltpr0 >= 2) {           // Need to combine the layers...
03173                 SelectDrawingLayer(1);  // so read layer 1 next
03174                 if (getPixelStream(pixelBuffer2, w, x,y+j) != noerror) {
03175                     ERR("getPixelStream error, and no recovery handler...");
03176                 }
03177             }
03178             //INFO("Line: %3d", j);
03179             //HexDump("Raster", (uint8_t *)pixelBuffer, w * sizeof(color_t));
03180             // Convert the local buffer to RGBQUAD format
03181             int lb = 0;
03182             for (int i=0; i<w; i++) {
03183                 color_t tColor = pixelBuffer[x+i];
03184                 tColor = (tColor >> 8) | (tColor << 8);     // Byte Swap
03185                 RGBQUAD q0 = RGB16ToRGBQuad(tColor);        // Scale to 24-bits
03186                 tColor = pixelBuffer2[x+i];
03187                 tColor = (tColor >> 8) | (tColor << 8);     // Byte Swap
03188                 RGBQUAD q1 = RGB16ToRGBQuad(tColor);        // Scale to 24-bits
03189                 switch (ltpr0) {
03190                     case 0:
03191                     case 1:
03192                     case 2: // lighten-overlay  (@TODO Not supported yet)
03193                     case 6: // Floating Windows     (@TODO not sure how to support)
03194                     default: // Reserved...
03195                         //lineBuffer[lb++] = q0.rgbBlue;
03196                         //lineBuffer[lb++] = q0.rgbGreen;
03197                         //lineBuffer[lb++] = q0.rgbRed;
03198                         break;
03199                     case 3: // transparent mode (@TODO Read the background color register for transparent)
03200                     case 4: // boolean or
03201                         q0.rgbBlue = q0.rgbBlue | q1.rgbBlue;
03202                         q0.rgbGreen = q0.rgbGreen | q1.rgbGreen;
03203                         q0.rgbRed = q0.rgbRed | q1.rgbRed;
03204                         break;
03205                     case 5: // boolean AND
03206                         q0.rgbBlue = q0.rgbBlue & q1.rgbBlue;
03207                         q0.rgbGreen = q0.rgbGreen & q1.rgbGreen;
03208                         q0.rgbRed = q0.rgbRed & q1.rgbRed;
03209                         break;
03210                 }
03211                 switch (bitsPerPixel) {
03212                     case 24:
03213                         lineBuffer[lb++] = q0.rgbBlue;
03214                         lineBuffer[lb++] = q0.rgbGreen;
03215                         lineBuffer[lb++] = q0.rgbRed;
03216                         break;
03217                     case 8:
03218                     default:
03219                         lineBuffer[lb++] = FindNearestWebColor(q0.rgbRed,q0.rgbGreen,q0.rgbBlue);
03220                         break;
03221                 }
03222             }
03223             //if (j == h - 1) {
03224             //    HexDump("Line", lineBuffer, lineBufSize);
03225             //}
03226             // Write to disk
03227             fwrite(lineBuffer, sizeof(char), lineBufSize, Image);
03228         }
03229         SelectDrawingLayer(prevLayer);
03230         fclose(Image);
03231         #ifndef DOUBLEBUF
03232         if (pixelBuffer2)
03233             swFree(pixelBuffer2);
03234         #endif
03235         if (pixelBuffer)
03236             swFree(pixelBuffer);
03237         swFree(lineBuffer);
03238         //INFO("Image closed");
03239         return noerror;
03240     } else {
03241         return bad_parameter;
03242     }
03243 }
03244 
03245 
03246 // ##########################################################################
03247 // ##########################################################################
03248 // ##########################################################################
03249 
03250 #ifdef TESTENABLE
03251 
03252 #include "BPG_Arial08x08.h"
03253 #include "BPG_Arial20x20.h"
03254 
03255 //      ______________  ______________  ______________  _______________
03256 //     /_____   _____/ /  ___________/ /  ___________/ /_____   ______/
03257 //          /  /      /  /            /  /                  /  /
03258 //         /  /      /  /___         /  /__________        /  /
03259 //        /  /      /  ____/        /__________   /       /  /
03260 //       /  /      /  /                       /  /       /  /
03261 //      /  /      /  /__________  ___________/  /       /  /
03262 //     /__/      /_____________/ /_____________/       /__/
03263 //
03264 //    Everything from here down is test code.
03265 //
03266 bool SuppressSlowStuff = false;
03267 
03268 void TextWrapTest(RA8875 & display, Serial & pc)
03269 {
03270     if (!SuppressSlowStuff)
03271         pc.printf("Text Wrap Test\r\n");
03272     display.background(Black);
03273     display.foreground(Blue);
03274     display.cls();
03275     display.Backlight_u8(255);
03276     display.puts("Text Wrap Test.\r\n");
03277     for (int i=1; i<60; i++) {
03278         display.printf("L%2d\n", i % 17);
03279         if (!SuppressSlowStuff)
03280             wait_us(100000);
03281     }
03282     if (!SuppressSlowStuff)
03283         wait_us(3000000);
03284 }
03285 
03286 
03287 void ShowKey(RA8875 & display, int key)
03288 {
03289     loc_t col, row;
03290     dim_t r1 = 25;
03291     color_t color = (key & 0x80) ? Red : Green;
03292 
03293     key &= 0x7F;        // remove the long-press flag
03294     row = (key - 1) / 5;
03295     col = (key - 1) % 5;
03296     if (col > 5) col = 5;
03297     if (row > 4) row = 4;
03298     display.circle(450 - + (2 * r1) * col, 200 - (2 * r1) * row, r1-2, color, FILL);
03299 }
03300 
03301 void HideKey(RA8875 & display, int key)
03302 {
03303     loc_t col, row;
03304     dim_t r1 = 25;
03305 
03306     row = (key - 1) / 5;
03307     col = (key - 1) % 5;
03308     if (col > 5) col = 5;
03309     if (row > 4) row = 4;
03310     display.background(Black);
03311     display.circle(450 - (2 * r1) * col, 200 - (2 * r1) * row, r1-2, Black, FILL);
03312     display.circle(450 - (2 * r1) * col, 200 - (2 * r1) * row, r1-2, Blue);
03313 }
03314 
03315 void KeyPadTest(RA8875 & display, Serial & pc)
03316 {
03317     const uint8_t myMap[22] = {
03318         0,
03319         'a', 'b', 'c', 'd', 'e',
03320         'f', 'g', 'h', 'i', 'j',
03321         'k', 'l', 'm', 'n', 'o',
03322         'p', 'q', 'r', 's', 't',
03323         'x'
03324     };
03325 
03326     display.background(Black);
03327     display.foreground(Blue);
03328     display.cls();
03329     display.Backlight_u8(255);
03330     display.puts("KeyPad Test. Touch the keypad...");
03331     pc.printf("\r\n"
03332               "Raw KeyPad Test. Keypad returns the key-number.\r\n"
03333               "Press [most] any PC keyboard key to advance to next test.\r\n");
03334     RetCode_t ret = display.KeypadInit(true, true, 3, 7, 3);
03335     if (ret != noerror)
03336         pc.printf("returncode from KeypadInit is %d\r\n", ret);
03337     int lastKey = 0;
03338     while (!pc.readable()) {
03339         if (display.readable()) {
03340             int key = display.getc();
03341             if (key) {
03342                 if (((key & 0x7F) != lastKey) && (lastKey != 0))
03343                     HideKey(display, lastKey);
03344                 ShowKey(display, key);
03345                 lastKey = key & 0x7F;
03346             } else {
03347                 // erase the last one
03348                 if (lastKey)
03349                     HideKey(display, lastKey);
03350             }
03351         }
03352     }
03353     (void)pc.getc();
03354     pc.printf("\r\n"
03355               "Map KeyPad Test. Keypad returns the remapped key 'a' - 't'.\r\n"
03356               "Press [most] any PC keyboard key to advance to exit test.\r\n");
03357     display.SetKeyMap(myMap);
03358     while (!pc.readable()) {
03359         if (display.readable()) {
03360             int key = display.getc();
03361             bool longPress = key & 0x80;
03362             display.SetTextCursor(0, 120);
03363             display.printf("Long Press: %d\r\n", longPress);
03364             display.printf("  Remapped: %c %02X\r\n", (key) ? key & 0x7F : ' ', key);
03365         }
03366     }
03367     (void)pc.getc();
03368     display.SetKeyMap();
03369     pc.printf("\r\n");
03370 }
03371 
03372 void TextCursorTest(RA8875 & display, Serial & pc)
03373 {
03374     const char * iCursor  = "The I-Beam cursor should be visible for this text.\r\n";
03375     const char * uCursor  = "The Underscore cursor should be visible for this text.\r\n";
03376     const char * bCursor  = "The Block cursor should be visible for this text.\r\n";
03377     const char * bbCursor = "The Blinking Block cursor should be visible for this text.\r\n";
03378     const char * p;
03379     int delay = 60;
03380 
03381     if (!SuppressSlowStuff)
03382         pc.printf("Text Cursor Test\r\n");
03383     else
03384         delay = 0;
03385     display.background(Black);
03386     display.foreground(Blue);
03387     display.cls();
03388     display.Backlight_u8(255);
03389     display.puts("Text Cursor Test.");
03390 
03391     // visible, non-blinking
03392     display.SetTextCursor(0,20);
03393     display.SetTextCursorControl(RA8875::IBEAM, false);
03394     p = iCursor;
03395     while (*p) {
03396         display._putc(*p++);
03397         wait_ms(delay);
03398     }
03399 
03400     display.SetTextCursorControl(RA8875::UNDER, false);
03401     p = uCursor;
03402     while (*p) {
03403         display._putc(*p++);
03404         wait_ms(delay);
03405     }
03406 
03407     display.SetTextCursorControl(RA8875::BLOCK, false);
03408     p = bCursor;
03409     while (*p) {
03410         display._putc(*p++);
03411         wait_ms(delay);
03412     }
03413 
03414     display.SetTextCursorControl(RA8875::BLOCK, true);
03415     p = bbCursor;
03416     while (*p) {
03417         display._putc(*p++);
03418         wait_ms(delay);
03419     }
03420     wait_ms(delay * 20);
03421     display.SetTextCursorControl(RA8875::NOCURSOR, false);
03422 }
03423 
03424 
03425 void BacklightTest(RA8875 & display, Serial & pc, float ramptime)
03426 {
03427     char buf[60];
03428     unsigned int w = (ramptime * 1000)/ 256;
03429     int delay = 200;
03430 
03431     if (!SuppressSlowStuff)
03432         pc.printf("Backlight Test - ramp over %f sec.\r\n", ramptime);
03433     else {
03434         delay = 0;
03435         w = 0;
03436     }
03437     display.Backlight_u8(0);
03438     display.background(White);
03439     display.foreground(Blue);
03440     display.cls();
03441     display.puts("RA8875 Backlight Test - Ramp up.");
03442     wait_ms(delay);
03443     for (int i=0; i <= 255; i++) {
03444         snprintf(buf, sizeof(buf), "%3d, %4u", i, w);
03445         display.puts(100,100,buf);
03446         display.Backlight_u8(i);
03447         wait_ms(w);
03448     }
03449 }
03450 
03451 
03452 void BacklightTest2(RA8875 & display, Serial & pc)
03453 {
03454     int delay = 20;
03455 
03456     if (!SuppressSlowStuff)
03457         pc.printf("Backlight Test 2\r\n");
03458     else
03459         delay = 0;
03460 
03461     // Dim it out at the end of the tests.
03462     display.foreground(Blue);
03463     display.puts(0,0, "Ramp Backlight down.");
03464     // Ramp it off
03465     for (int i=255; i != 0; i--) {
03466         display.Backlight_u8(i);
03467         wait_ms(delay);
03468     }
03469     display.Backlight_u8(0);
03470 }
03471 
03472 
03473 void ExternalFontTest(RA8875 & display, Serial & pc)
03474 {
03475     if (!SuppressSlowStuff)
03476         pc.printf("External Font Test\r\n");
03477     display.background(Black);
03478     display.foreground(Blue);
03479     display.cls();
03480     display.puts("External Font Test.");
03481     display.Backlight(1);
03482 
03483     display.SelectUserFont(BPG_Arial08x08);
03484     display.puts(0,30, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\r\n");
03485 
03486     display.SelectUserFont(BPG_Arial20x20);
03487     display.puts("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\r\n");
03488 
03489     display.SelectUserFont();
03490 
03491     display.puts("Normal font again.");
03492     //display.window(0,0, display.width(), display.height());
03493 }
03494 
03495 
03496 void DOSColorTest(RA8875 & display, Serial & pc)
03497 {
03498     if (!SuppressSlowStuff)
03499         pc.printf("DOS Color Test\r\n");
03500     display.background(Black);
03501     display.foreground(Blue);
03502     display.cls();
03503     display.puts("DOS Colors - Fore");
03504     display.puts(280,0, "Back");
03505     display.background(Gray);
03506     for (int i=0; i<16; i++) {
03507         display.foreground(display.DOSColor(i));
03508         display.puts(160, i*16, display.DOSColorNames(i));
03509         display.background(Black);
03510     }
03511     display.foreground(White);
03512     for (int i=0; i<16; i++) {
03513         display.background(display.DOSColor(i));
03514         display.puts(360, i*16, display.DOSColorNames(i));
03515         display.foreground(White);
03516     }
03517 }
03518 
03519 
03520 void WebColorTest(RA8875 & display, Serial & pc)
03521 {
03522     if (!SuppressSlowStuff)
03523         pc.printf("Web Color Test\r\n");
03524     display.background(Black);
03525     display.foreground(Blue);
03526     display.window(0,0, display.width(), display.height());
03527     display.cls();
03528     display.SetTextFontSize(1,1);
03529     display.puts(200,0, "Web Color Test");
03530     display.SetTextCursor(0,0);
03531     display.puts("  ");
03532     for (int i=0; i<16; i++)
03533         display.printf("%X", i&0xF);
03534     display.puts("\r\n0 ");
03535     for (int i=0; i<sizeof(WebColors)/sizeof(WebColors[0]); i++) {
03536         display.background(WebColors[i]);
03537         display.puts(" ");
03538         if (i % 16 == 15 && i < 255) {
03539             display.printf("\r\n%X ", ((i+1)/16));
03540         }
03541     }
03542     display.SetTextFontSize(1,1);
03543 }
03544 
03545 
03546 void PixelTest(RA8875 & display, Serial & pc)
03547 {
03548     int i, c, x, y;
03549 
03550     if (!SuppressSlowStuff)
03551         pc.printf("Pixel Test\r\n");
03552     display.background(Black);
03553     display.foreground(Blue);
03554     display.cls();
03555     display.puts("Pixel Test");
03556     for (i=0; i<1000; i++) {
03557         x = rand() % 480;
03558         y = 16 + rand() % (272-16);
03559         c = rand() % 16;
03560         //pc.printf("  (%d,%d) - %d\r\n", x,y,r1);
03561         display.pixel(x,y, display.DOSColor(c));
03562     }
03563 }
03564 
03565 
03566 void LineTest(RA8875 & display, Serial & pc)
03567 {
03568     int i, x, y, x2, y2;
03569 
03570     if (!SuppressSlowStuff)
03571         pc.printf("Line Test\r\n");
03572     display.background(Black);
03573     display.foreground(Blue);
03574     display.cls();
03575     display.puts("Line Test");
03576     for (i=0; i<16; i++) {
03577         // Lines
03578         x = rand() % 480;
03579         y = rand() % 272;
03580         x2 = rand() % 480;
03581         y2 = rand() % 272;
03582         display.line(x,y, x2,y2, display.DOSColor(i));
03583     }
03584     display.foreground(BrightRed);
03585     display.foreground(BrightGreen);
03586     display.foreground(BrightBlue);
03587     display.line(55,50, 79,74, BrightRed);
03588     display.line(57,50, 81,74, BrightGreen);
03589     display.line(59,50, 83,74, BrightBlue);
03590     // horz
03591     display.line(30,40, 32,40, BrightRed);
03592     display.line(30,42, 32,42, BrightGreen);
03593     display.line(30,44, 32,44, BrightBlue);
03594     // vert
03595     display.line(20,40, 20,42, BrightRed);
03596     display.line(22,40, 22,42, BrightGreen);
03597     display.line(24,40, 24,42, BrightBlue);
03598     // compare point to line-point
03599     display.pixel(20,50, BrightRed);
03600     display.pixel(22,50, BrightGreen);
03601     display.pixel(24,50, BrightBlue);
03602     display.line(20,52, 20,52, BrightRed);
03603     display.line(22,52, 22,52, BrightGreen);
03604     display.line(24,52, 24,52, BrightBlue);
03605 
03606     // point
03607     display.line(50,50, 50,50, Red);
03608     display.line(52,52, 52,52, Green);
03609     display.line(54,54, 54,54, Blue);
03610     display.line(60,60, 60,60, BrightRed);
03611     display.line(62,62, 62,62, BrightGreen);
03612     display.line(64,64, 64,64, BrightBlue);
03613     display.line(70,70, 70,70, DarkRed);
03614     display.line(72,72, 72,72, DarkGreen);
03615     display.line(74,74, 74,74, DarkBlue);
03616 }
03617 
03618 
03619 void RectangleTest(RA8875 & display, Serial & pc)
03620 {
03621     int i, x1,y1, x2,y2;
03622 
03623     if (!SuppressSlowStuff)
03624         pc.printf("Rectangle Test\r\n");
03625     display.background(Black);
03626     display.foreground(Blue);
03627     display.cls();
03628     display.puts("Rectangle Test");
03629     for (i=0; i<16; i++) {
03630         x1 = rand() % 240;
03631         y1 = 50 + rand() % 200;
03632         x2 = rand() % 240;
03633         y2 = 50 + rand() % 200;
03634         display.rect(x1,y1, x2,y2, display.DOSColor(i));
03635 
03636         x1 = 240 + rand() % 240;
03637         y1 = 50 + rand() % 200;
03638         x2 = 240 + rand() % 240;
03639         y2 = 50 + rand() % 200;
03640         display.rect(x1,y1, x2,y2, FILL);
03641     }
03642 }
03643 
03644 
03645 void LayerTest(RA8875 & display, Serial & pc)
03646 {
03647     loc_t i, x1,y1, x2,y2, r1,r2;
03648 
03649     if (!SuppressSlowStuff)
03650         pc.printf("Layer Test\r\n");
03651 
03652     display.SelectDrawingLayer(0);
03653     display.background(Black);
03654     display.foreground(Blue);
03655     display.cls();
03656     display.puts("Layer 0");
03657     for (i=0; i<16; i++) {
03658         x1 = rand() % 240;
03659         y1 = 50 + rand() % 200;
03660         x2 = x1 + rand() % 100;
03661         y2 = y1 + rand() % 100;
03662         r1 = rand() % (x2 - x1)/2;
03663         r2 = rand() % (y2 - y1)/2;
03664         display.roundrect(x1,y1, x2,y2, r1,r2, display.DOSColor(i));
03665         if (!SuppressSlowStuff)
03666             wait_us(20000);
03667     }
03668     if (!SuppressSlowStuff)
03669         wait_us(1000000);
03670 
03671     display.SelectDrawingLayer(1);
03672     display.background(Black);
03673     display.foreground(Yellow);
03674     display.cls();
03675     display.puts(240,0, "Layer 1");
03676     for (i=0; i<16; i++) {
03677         x1 = 300 + rand() % 100;
03678         y1 = 70 + rand() % 200;
03679         r1 = rand() % RAmin(y1 - 20, 100);
03680         display.circle(x1,y1,r1, display.DOSColor(i));
03681         if (!SuppressSlowStuff)
03682             wait_us(20000);
03683     }
03684     display.SetLayerMode(RA8875::ShowLayer1);        // Show it after the build-up
03685     if (!SuppressSlowStuff)
03686         wait_us(2000000);
03687 
03688     display.SelectDrawingLayer(0);
03689     display.SetLayerMode(RA8875::ShowLayer0);        // Show Layer 0 again
03690     if (!SuppressSlowStuff)
03691         wait_us(1000000);
03692     display.SetLayerMode(RA8875::TransparentMode);        // Transparent mode
03693     if (!SuppressSlowStuff)
03694         wait_us(1000000);
03695     for (i=0; i<=8; i++) {
03696         display.SetLayerTransparency(i, 8-i);
03697         if (!SuppressSlowStuff)
03698             wait_us(200000);
03699     }
03700 
03701     // Restore before we exit
03702     display.SetLayerTransparency(0, 0);
03703     display.SetLayerMode(RA8875::ShowLayer0);        // Restore to layer 0
03704 }
03705 
03706 
03707 void RoundRectTest(RA8875 & display, Serial & pc)
03708 {
03709     loc_t i, x1,y1, x2,y2, r1,r2;
03710 
03711     if (!SuppressSlowStuff)
03712         pc.printf("Round Rectangle Test\r\n");
03713     display.background(Black);
03714     display.foreground(Blue);
03715     display.cls();
03716     display.puts("Rounded Rectangle Test");
03717 
03718     for (i=0; i<16; i++) {
03719         x1 = rand() % 240;
03720         y1 = 50 + rand() % 200;
03721         x2 = x1 + rand() % 100;
03722         y2 = y1 + rand() % 100;
03723         r1 = rand() % (x2 - x1)/2;
03724         r2 = rand() % (y2 - y1)/2;
03725         display.roundrect(x1,y1, x2,y2, 5,8, display.DOSColor(i));
03726 
03727         x1 = 240 + rand() % 240;
03728         y1 = 50 + rand() % 200;
03729         x2 = x1 + rand() % 100;
03730         y2 = y1 + rand() % 100;
03731         r1 = rand() % (x2 - x1)/2;
03732         r2 = rand() % (y2 - y1)/2;
03733         display.roundrect(x1,y1, x2,y2, r1,r2, FILL);
03734     }
03735 }
03736 
03737 
03738 void TriangleTest(RA8875 & display, Serial & pc)
03739 {
03740     int i, x1, y1, x2, y2, x3, y3;
03741 
03742     if (!SuppressSlowStuff)
03743         pc.printf("Triangle Test\r\n");
03744     display.background(Black);
03745     display.foreground(Blue);
03746     display.cls();
03747     display.puts(0,0, "Triangle Test");
03748 
03749     x1 = 150;
03750     y1 = 2;
03751     x2 = 190;
03752     y2 = 7;
03753     x3 = 170;
03754     y3 = 16;
03755     display.triangle(x1,y1, x2,y2, x3,y3);
03756 
03757     x1 = 200;
03758     y1 = 2;
03759     x2 = 240;
03760     y2 = 7;
03761     x3 = 220;
03762     y3 = 16;
03763     display.filltriangle(x1,y1, x2,y2, x3,y3, BrightRed);
03764 
03765     x1 = 300;
03766     y1 = 2;
03767     x2 = 340;
03768     y2 = 7;
03769     x3 = 320;
03770     y3 = 16;
03771     display.triangle(x1,y1, x2,y2, x3,y3, NOFILL);
03772 
03773     x1 = 400;
03774     y1 = 2;
03775     x2 = 440;
03776     y2 = 7;
03777     x3 = 420;
03778     y3 = 16;
03779     display.triangle(x1,y1, x2,y2, x3,y3, Blue);
03780 
03781     for (i=0; i<16; i++) {
03782         x1 = rand() % 240;
03783         y1 = 50 + rand() % 200;
03784         x2 = rand() % 240;
03785         y2 = 50 + rand() % 200;
03786         x3 = rand() % 240;
03787         y3 = 50 + rand() % 200;
03788         display.triangle(x1,y1, x2,y2, x3,y3, display.DOSColor(i));
03789         x1 = 240 + rand() % 240;
03790         y1 = 50 + rand() % 200;
03791         x2 = 240 + rand() % 240;
03792         y2 = 50 + rand() % 200;
03793         x3 = 240 + rand() % 240;
03794         y3 = 50 + rand() % 200;
03795         display.triangle(x1,y1, x2,y2, x3,y3, FILL);
03796     }
03797 }
03798 
03799 
03800 void CircleTest(RA8875 & display, Serial & pc)
03801 {
03802     int i, x, y, r1;
03803 
03804     if (!SuppressSlowStuff)
03805         pc.printf("Circle Test\r\n");
03806     display.background(Black);
03807     display.foreground(Blue);
03808     display.cls();
03809     display.puts("Circle Test");
03810     for (i=0; i<16; i++) {
03811         x = 100 + rand() % 100;
03812         y = 70 + rand() % 200;
03813         r1 = rand() % RAmin(y - 20, 100);
03814         //pc.printf("  (%d,%d) - %d\r\n", x,y,r1);
03815         display.circle(x,y,r1, display.DOSColor(i));
03816 
03817         x = 300 + rand() % 100;
03818         y = 70 + rand() % 200;
03819         r1 = rand() % RAmin(y - 20, 100);
03820         //pc.printf("  (%d,%d) - %d FILL\r\n", x,y,r1);
03821         display.circle(x,y,r1, display.DOSColor(i), FILL);
03822     }
03823 }
03824 
03825 
03826 void EllipseTest(RA8875 & display, Serial & pc)
03827 {
03828     int i,x,y,r1,r2;
03829 
03830     if (!SuppressSlowStuff)
03831         pc.printf("Ellipse Test\r\n");
03832     display.background(Black);
03833     display.foreground(Blue);
03834     display.cls();
03835     display.puts("Ellipse Test");
03836     for (i=0; i<16; i++) {
03837         x = 100 + rand() % 100;
03838         y = 70 + rand() % 200;
03839         r1 = rand() % RAmin(y - 20, 100);
03840         r2 = rand() % RAmin(y - 20, 100);
03841         display.ellipse(x,y,r1,r2, display.DOSColor(i));
03842 
03843         x = 300 + rand() % 100;
03844         y = 70 + rand() % 200;
03845         r1 = rand() % RAmin(y - 20, 100);
03846         r2 = rand() % RAmin(y - 20, 100);
03847         display.ellipse(x,y,r1,r2, FILL);
03848     }
03849 }
03850 
03851 
03852 void TestGraphicsBitmap(RA8875 & display, Serial & pc)
03853 {
03854     LocalFileSystem local("local");
03855     if (!SuppressSlowStuff)
03856         pc.printf("Bitmap File Load\r\n");
03857     display.background(Black);
03858     display.foreground(Blue);
03859     display.cls();
03860     display.puts("Graphics Test, loading /local/TestPat.bmp");
03861     wait(3);
03862 
03863     int r = display.RenderBitmapFile(0,0, "/local/TestPat.bmp");
03864     if (!SuppressSlowStuff)
03865         pc.printf("  returned %d\r\n", r);
03866 }
03867 
03868 
03869 void TouchPanelTest(RA8875 & display, Serial & pc)
03870 {
03871     Timer t;
03872     int x, y;
03873     tpMatrix_t calmatrix;
03874 
03875     display.background(Black);
03876     display.foreground(Blue);
03877     display.cls();
03878     display.puts("Touch Panel Test\r\n");
03879     pc.printf("Touch Panel Test\r\n");
03880     display.TouchPanelInit();
03881     pc.printf("  TP: c - calibrate, r - restore, t - test\r\n");
03882     int c = pc.getc();
03883     if (c == 'c') {
03884         point_t pTest[3] =
03885         { { 50, 50 }, {450, 150}, {225,250} };
03886         point_t pSample[3];
03887         for (int i=0; i<3; i++) {
03888             display.foreground(Blue);
03889             display.printf(" (%3d,%3d) => ", pTest[i].x, pTest[i].y);
03890             display.line(pTest[i].x-10, pTest[i].y, pTest[i].x+10, pTest[i].y, White);
03891             display.line(pTest[i].x, pTest[i].y-10, pTest[i].x, pTest[i].y+10, White);
03892             while (!display.TouchPanelA2DFiltered(&x, &y))
03893                 wait_us(20000);
03894             pSample[i].x = x;
03895             pSample[i].y = y;
03896             display.line(pTest[i].x-10, pTest[i].y, pTest[i].x+10, pTest[i].y, Black);
03897             display.line(pTest[i].x, pTest[i].y-10, pTest[i].x, pTest[i].y+10, Black);
03898             display.foreground(Blue);
03899             display.printf(" (%4d,%4d)\r\n", x,y);
03900             while (display.TouchPanelA2DFiltered(&x, &y))
03901                 wait_us(20000);
03902             wait(2);
03903         }
03904         display.TouchPanelComputeCalibration(pTest, pSample, &calmatrix);
03905         display.printf(" Writing calibration to tpcal.cfg\r\n");
03906         FILE * fh = fopen("/local/tpcal.cfg", "wb");
03907         if (fh) {
03908             fwrite(&calmatrix, sizeof(calmatrix), 1, fh);
03909             fclose(fh);
03910         }
03911         display.printf(" Calibration is complete.");
03912     } else if (c == 'r') {
03913         display.printf(" Reading calibration from tpcal.cfg\r\n");
03914         FILE * fh = fopen("/local/tpcal.cfg", "rb");
03915         if (fh) {
03916             fread(&calmatrix, sizeof(calmatrix), 1, fh);
03917             fclose(fh);
03918         }
03919         display.printf(" Calibration is complete.");
03920         display.TouchPanelSetMatrix(&calmatrix);
03921     }
03922     t.start();
03923     do {
03924         point_t point = {0, 0};
03925         if (display.TouchPanelReadable(&point)) {
03926             display.pixel(point.x, point.y, Red);
03927         }
03928     } while (t.read_ms() < 30000);
03929     pc.printf(">");
03930 }
03931 
03932 
03933 void SpeedTest(RA8875 & display, Serial & pc)
03934 {
03935     Timer t;
03936     SuppressSlowStuff = true;
03937     pc.printf("\r\nSpeedTest disables delays, runs tests, reports overall time.\r\n");
03938     t.start();
03939     // do stuff fast
03940     TextCursorTest(display, pc);
03941     TextWrapTest(display, pc);
03942     BacklightTest(display, pc, 0);
03943     BacklightTest2(display, pc);
03944     ExternalFontTest(display, pc);
03945     DOSColorTest(display, pc);
03946     WebColorTest(display, pc);
03947     PixelTest(display, pc);
03948     LineTest(display, pc);
03949     RectangleTest(display, pc);
03950     RoundRectTest(display, pc);
03951     TriangleTest(display, pc);
03952     CircleTest(display, pc);
03953     EllipseTest(display, pc);
03954     LayerTest(display, pc);
03955     //TestGraphicsBitmap(display, pc);
03956     pc.printf("SpeedTest completed in %d msec\r\n", t.read_ms());
03957 #ifdef PERF_METRICS
03958     display.ReportPerformance(pc);
03959 #endif
03960     SuppressSlowStuff = false;
03961 }
03962 
03963 
03964 void PrintScreen(RA8875 & display, Serial & pc)
03965 {
03966     if (!SuppressSlowStuff)
03967         pc.printf("PrintScreen\r\n");
03968     display.PrintScreen( 0,0, 480,272, "/local/Capture.bmp");
03969 }
03970 
03971 
03972 void RunTestSet(RA8875 & lcd, Serial & pc)
03973 {
03974     int q = 0;
03975     int automode = 0;
03976     const unsigned char modelist[] = "BDWtGLlFROTPCEbw";   // auto-test in this order.
03977 
03978     while(1) {
03979         pc.printf("\r\n"
03980                   "B - Backlight up      b - backlight dim\r\n"
03981                   "D - DOS Colors        W - Web Colors\r\n"
03982                   "t - text cursor       G - Graphics Bitmap\r\n"
03983                   "L - Lines             F - external Font\r\n"
03984                   "R - Rectangles        O - rOund rectangles\r\n"
03985                   "T - Triangles         P - Pixels  \r\n"
03986                   "C - Circles           E - Ellipses\r\n"
03987                   "A - Auto Test mode    S - Speed Test\r\n"
03988                   "K - Keypad Test       s - touch screen test\r\n"
03989                   "p - print screen      r - reset  \r\n"
03990                   "l - layer test        w - wrapping text \r\n"
03991 #ifdef PERF_METRICS
03992                   "0 - clear performance 1 - report performance\r\n"
03993 #endif
03994                   "> ");
03995         if (automode == -1 || pc.readable()) {
03996             automode = -1;
03997             q = pc.getc();
03998             while (pc.readable())
03999                 pc.getc();
04000         } else if (automode >= 0) {
04001             q = modelist[automode];
04002         }
04003         switch(q) {
04004 #ifdef PERF_METRICS
04005             case '0':
04006                 lcd.ClearPerformance();
04007                 break;
04008             case '1':
04009                 lcd.ReportPerformance(pc);
04010                 break;
04011 #endif
04012             case 'A':
04013                 automode = 0;
04014                 break;
04015             case 'B':
04016                 BacklightTest(lcd, pc, 2);
04017                 break;
04018             case 'b':
04019                 BacklightTest2(lcd, pc);
04020                 break;
04021             case 'D':
04022                 DOSColorTest(lcd, pc);
04023                 break;
04024             case 'K':
04025                 KeyPadTest(lcd, pc);
04026                 break;
04027             case 'W':
04028                 WebColorTest(lcd, pc);
04029                 break;
04030             case 't':
04031                 TextCursorTest(lcd, pc);
04032                 break;
04033             case 'w':
04034                 TextWrapTest(lcd, pc);
04035                 break;
04036             case 'F':
04037                 ExternalFontTest(lcd, pc);
04038                 break;
04039             case 'L':
04040                 LineTest(lcd, pc);
04041                 break;
04042             case 'l':
04043                 LayerTest(lcd, pc);
04044                 break;
04045             case 'R':
04046                 RectangleTest(lcd, pc);
04047                 break;
04048             case 'O':
04049                 RoundRectTest(lcd, pc);
04050                 break;
04051             case 'p':
04052                 PrintScreen(lcd, pc);
04053                 break;
04054             case 'S':
04055                 SpeedTest(lcd, pc);
04056                 break;
04057             case 's':
04058                 TouchPanelTest(lcd, pc);
04059                 break;
04060             case 'T':
04061                 TriangleTest(lcd, pc);
04062                 break;
04063             case 'P':
04064                 PixelTest(lcd, pc);
04065                 break;
04066             case 'G':
04067                 TestGraphicsBitmap(lcd, pc);
04068                 break;
04069             case 'C':
04070                 CircleTest(lcd, pc);
04071                 break;
04072             case 'E':
04073                 EllipseTest(lcd, pc);
04074                 break;
04075             case 'r':
04076                 pc.printf("Resetting ...\r\n");
04077                 wait_us(20000);
04078                 mbed_reset();
04079                 break;
04080             case ' ':
04081                 break;
04082             default:
04083                 printf("huh?\n");
04084                 break;
04085         }
04086         if (automode >= 0) {
04087             automode++;
04088             if (automode >= sizeof(modelist))
04089                 automode = 0;
04090             wait_us(2000000);
04091         }
04092         wait_us(200000);
04093     }
04094 }
04095 
04096 #endif // TESTENABLE