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

See Components - RA8875 Based Display

Enhanced touch-screen support - where it previous supported both the Resistive Touch and Capacitive Touch based on the FT5206 Touch Controller, now it also has support for the GSL1680 Touch Controller.

Offline Help Manual (Windows chm)

/media/uploads/WiredHome/ra8875.zip.bin (download, rename to .zip and unzip)

Revision:
190:3132b7dfad82
Parent:
186:910fc2335c45
Child:
191:0fad2e45e196
--- a/RA8875.cpp	Thu Sep 19 21:45:18 2019 +0000
+++ b/RA8875.cpp	Sat Sep 21 17:30:00 2019 +0000
@@ -23,9 +23,9 @@
 // INFO("Stuff to show %d", var); // new-line is automatically appended
 //
 #if (defined(DEBUG) && !defined(TARGET_LPC11U24))
-#define INFO(x, ...) std::printf("[INF %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
-#define WARN(x, ...) std::printf("[WRN %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
-#define ERR(x, ...)  std::printf("[ERR %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
+#define INFO(x, ...) std::printf("[INF %s %4d] " x "\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
+#define WARN(x, ...) std::printf("[WRN %s %4d] " x "\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
+#define ERR(x, ...)  std::printf("[ERR %s %4d] " x "\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
 static void HexDump(const char * title, const uint8_t * p, int count)
 {
     int i;
@@ -151,13 +151,13 @@
     {0xFF,0xCC,0x00,0xFF}, {0xFF,0xCC,0x33,0xFF}, {0xFF,0xCC,0x66,0xFF}, {0xFF,0xCC,0x99,0xFF}, {0xFF,0xCC,0xCC,0xFF}, {0xFF,0xCC,0xFF,0xFF},
     {0xFF,0xFF,0x00,0xFF}, {0xFF,0xFF,0x33,0xFF}, {0xFF,0xFF,0x66,0xFF}, {0xFF,0xFF,0x99,0xFF}, {0xFF,0xFF,0xCC,0xFF}, {0xFF,0xFF,0xFF,0xFF},
 
-    {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, 
-    {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, 
-    {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, 
-    {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, 
-    {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, 
-    {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, 
-    {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, 
+    {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF},
+    {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF},
+    {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF},
+    {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF},
+    {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF},
+    {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF},
+    {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF}, {0x00,0x00,0x00,0xFF},
 };
 
 #define sqr(a) ((a) * (a))
@@ -185,7 +185,7 @@
 
 // Non-Touch, or Resistive Touch when later initialized that way
 //
-RA8875::RA8875(PinName mosi, PinName miso, PinName sclk, PinName csel, PinName reset, 
+RA8875::RA8875(PinName mosi, PinName miso, PinName sclk, PinName csel, PinName reset,
     const char *name)
     : GraphicsDisplay(name)
     , spi(mosi, miso, sclk)
@@ -201,8 +201,8 @@
 
 // Touch, based on FT5206 Controller Chip
 //
-RA8875::RA8875(PinName mosi, PinName miso, PinName sclk, PinName csel, PinName reset, 
-    PinName sda, PinName scl, PinName irq, const char * name) 
+RA8875::RA8875(PinName mosi, PinName miso, PinName sclk, PinName csel, PinName reset,
+    PinName sda, PinName scl, PinName irq, const char * name)
     : GraphicsDisplay(name)
     , spi(mosi, miso, sclk)
     , cs(csel)
@@ -213,14 +213,14 @@
     m_irq = new InterruptIn(irq);
     m_i2c = new I2C(sda, scl);
     INFO("m_i2c = %p", m_i2c);
-    
+
     // Cap touch panel config
     touchInfo = (touchInfo_T *)malloc(FT5206_TOUCH_POINTS * sizeof(touchInfo_T));
     if (touchInfo)
         useTouchPanel = TP_FT5206;
     m_addr = (FT5206_I2C_ADDRESS << 1);
     m_i2c->frequency(FT5206_I2C_FREQUENCY);
-    
+
     // Interrupt
     m_irq->mode(PullUp);
     #if MBED_VERSION >= MBED_ENCODE_VERSION(5,8,0)
@@ -239,8 +239,8 @@
 
 // Touch, based on GSL1680 Controller Chip
 //
-RA8875::RA8875(PinName mosi, PinName miso, PinName sclk, PinName csel, PinName reset, 
-    PinName sda, PinName scl, PinName wake, PinName irq, const char * name) 
+RA8875::RA8875(PinName mosi, PinName miso, PinName sclk, PinName csel, PinName reset,
+    PinName sda, PinName scl, PinName wake, PinName irq, const char * name)
     : GraphicsDisplay(name)
     , spi(mosi, miso, sclk)
     , cs(csel)
@@ -248,7 +248,7 @@
 {
     INFO("RA8875");
     InitAllMemberVars();
-    
+
     m_irq = new InterruptIn(irq);
     m_i2c = new I2C(sda, scl);
 
@@ -315,6 +315,7 @@
     cursor_x = 0;
     cursor_y = 0;
     _printFH = NULL;
+    roundCap = false;
     screen_orientation = normal;
 #ifdef PERF_METRICS
     ClearPerformance();
@@ -429,7 +430,7 @@
 RetCode_t RA8875::Reset(void)
 {
     RetCode_t ret;
-    
+
     #if 0
     if (res != (PinName)NC) {
         res = 0;                            // Active low - assert reset
@@ -464,10 +465,10 @@
 RetCode_t RA8875::SelectDrawingLayer(uint16_t layer, uint16_t * prevLayer)
 {
     unsigned char mwcr1 = ReadCommand(0x41); // retain all but the currently selected layer
-    
+
     if (prevLayer)
         *prevLayer = mwcr1 & 1;
-    
+
     mwcr1 &= ~0x01; // remove the current layer
     if (screenwidth >= 800 && screenheight >= 480 && screenbpp > 8) {
         layer = 0;
@@ -487,7 +488,7 @@
 RetCode_t RA8875::SetLayerMode(LayerMode_T mode)
 {
     unsigned char ltpr0 = ReadCommand(0x52) & ~0x7; // retain all but the display layer mode
-    
+
     if (mode <= (LayerMode_T)6) {
         WriteCommand(RA8875_LTPR0, ltpr0 | (mode & 0x7));
         return noerror;
@@ -517,7 +518,7 @@
 color_t RA8875::GetBackgroundTransparencyColor(void)
 {
     RGBQUAD q;
-    
+
     q.rgbRed = ReadCommand(0x67);
     q.rgbGreen = ReadCommand(0x68);
     q.rgbBlue = ReadCommand(0x69);
@@ -573,7 +574,7 @@
     static uint8_t count = 0;
     uint8_t col, row;
     uint8_t key;
-    
+
     while (!readable()) {
         wait_us(POLLWAITuSec);
         // COUNTIDLETIME(POLLWAITuSec);     // As it is voluntary to call the getc and pend. Don't tally it.
@@ -646,7 +647,7 @@
 void RA8875::ClearPerformance()
 {
     int i;
-    
+
     for (i=0; i<METRICCOUNT; i++)
         metrics[i] = 0;
     idletime_usec = 0;
@@ -673,7 +674,7 @@
 void RA8875::ReportPerformance(Serial & pc)
 {
     int i;
-    
+
     pc.printf("\r\nPerformance Metrics\r\n");
     for (i=0; i<METRICCOUNT; i++) {
         pc.printf("%10d uS %s\r\n", metrics[i], metricsName[i]);
@@ -687,6 +688,44 @@
 #endif
 
 
+rect_t RA8875::AlignRectInRect(rect_t toAlign, rect_t inRect, valign_t v, halign_t h) {
+    rect_t newRect; dim_t width; dim_t height;
+    width = toAlign.p2.x - toAlign.p1.x;
+    height = toAlign.p2.y - toAlign.p1.y;
+    switch (h) {
+        case left:
+        default:
+            newRect.p1.x = inRect.p1.x;
+            newRect.p2.x = newRect.p1.x + width;
+            break;
+        case center:
+            newRect.p1.x = (inRect.p1.x + inRect.p2.x + 1) / 2 - width / 2;
+            newRect.p2.x = newRect.p1.x + width;
+            break;
+        case right:
+            newRect.p1.x = inRect.p2.x - width;
+            newRect.p2.x = newRect.p1.x + width;
+            break;
+    }
+    switch (v) {
+        case top:
+        default:
+            newRect.p1.y = inRect.p1.y;
+            newRect.p2.y = newRect.p1.y + height;
+            break;
+        case middle:
+            newRect.p1.y = (inRect.p1.y + inRect.p2.y + 1) / 2 - height / 2;
+            newRect.p2.y = newRect.p1.y + height;
+            break;
+        case bottom:
+            newRect.p1.y = inRect.p2.y - height;
+            newRect.p2.y = newRect.p1.y + height;
+            break;
+    }
+    return newRect;
+}
+
+
 bool RA8875::Intersect(rect_t rect, point_t p)
 {
     if (p.x >= min(rect.p1.x, rect.p2.x) && p.x <= max(rect.p1.x, rect.p2.x)
@@ -728,7 +767,7 @@
 {
     if (Intersect(*pRect1, *pRect2)) {
         rect_t iSect;
-        
+
         iSect.p1.x = max(min(pRect1->p1.x,pRect1->p2.x),min(pRect2->p1.x,pRect2->p2.x));
         iSect.p1.y = max(min(pRect1->p1.y,pRect1->p2.y),min(pRect2->p1.y,pRect2->p2.y));
         iSect.p2.x = min(max(pRect1->p1.x,pRect1->p2.x),max(pRect2->p1.x,pRect2->p2.x));
@@ -899,7 +938,7 @@
 {
     color_t c16;
     color_t temp = (color_t)c8;
-    
+
     c16 = ((temp & 0xE0) << 8)
         | ((temp & 0xC0) << 5)
         | ((temp & 0x1C) << 6)
@@ -914,14 +953,14 @@
 RetCode_t RA8875::_writeColorTrio(uint8_t regAddr, color_t color)
 {
     RetCode_t rt = noerror;
-    
+
     if (screenbpp == 16) {
         WriteCommand(regAddr+0, (color>>11));                  // BGCR0
         WriteCommand(regAddr+1, (unsigned char)(color>>5));    // BGCR1
         rt = WriteCommand(regAddr+2, (unsigned char)(color));       // BGCR2
     } else {
         uint8_t r, g, b;
-        
+
         // RRRR RGGG GGGB BBBB      RGB
         // RRR   GGG    B B
         r = (uint8_t)((color) >> 13);
@@ -938,7 +977,7 @@
 {
     color_t color;
     uint8_t r, g, b;
-    
+
     r = ReadCommand(regAddr+0);
     g = ReadCommand(regAddr+1);
     b = ReadCommand(regAddr+2);
@@ -1018,6 +1057,12 @@
     return screenbpp;
 }
 
+bool RA8875::SetWordWrap(bool _wordwrap) {
+    bool prevWrap = wordwrap;
+    wordwrap = _wordwrap;
+    return prevWrap;
+}
+
 RetCode_t RA8875::SetTextCursor(point_t p)
 {
     return SetTextCursor(p.x, p.y);
@@ -1036,7 +1081,7 @@
 point_t RA8875::GetTextCursor(void)
 {
     point_t p;
-    
+
     p.x = GetTextCursor_X();
     p.y = GetTextCursor_Y();
     return p;
@@ -1045,7 +1090,7 @@
 loc_t RA8875::GetTextCursor_Y(void)
 {
     loc_t y;
-    
+
     if (font == NULL)
         y = ReadCommand(0x2C) | (ReadCommand(0x2D) << 8);
     else
@@ -1058,7 +1103,7 @@
 loc_t RA8875::GetTextCursor_X(void)
 {
     loc_t x;
-    
+
     if (font == NULL)
         x = ReadCommand(0x2A) | (ReadCommand(0x2B) << 8);
     else
@@ -1122,7 +1167,7 @@
     uint8_t fncr1Val = ReadCommand(0x22);
     uint8_t dpcrVal = ReadCommand(0x20);
     RetCode_t r;
-    
+
     screen_orientation = angle;
     fncr1Val &= ~0x10;      // remove the old font rotation bit
     dpcrVal &= ~0x0C;       // remove the old scan direction bits
@@ -1165,7 +1210,7 @@
     if (hScale >= 1 && hScale <= 4 &&
             vScale >= 1 && vScale <= 4) {
         uint8_t fncr1Val = ReadCommand(0x22);
-        
+
         fncr1Val &= 0x10;      // remove all except the font rotation bit
         if (alignment == align_full)
             fncr1Val |= 0x80;
@@ -1179,6 +1224,19 @@
     }
 }
 
+fill_t RA8875::SetTextFontFill(fill_t fillit)
+{
+    fill_t prevFill = FILL;
+    uint8_t fncr1Val = ReadCommand(0x22);
+
+    if (fncr1Val & 0x40)
+        prevFill = NOFILL;
+    fncr1Val &= ~0x40;
+    if (fillit == NOFILL)
+        fncr1Val |= 0x40;
+    WriteCommand(RA8875_FNCR1, fncr1Val);
+    return prevFill;
+}
 
 RetCode_t RA8875::SetTextFontSize(RA8875::HorizontalScale hScale, RA8875::VerticalScale vScale)
 {
@@ -1204,12 +1262,34 @@
     unsigned char reg = ReadCommand(0x22);
 
     if (hScale)
-        *hScale = 1 + (reg >> 2) & 0x03;
+        *hScale = 1 + ((reg >> 2) & 0x03);
     if (vScale)
-        *vScale = 1 + reg & 0x03;
+        *vScale = 1 + (reg & 0x03);
     return noerror;
 }
 
+dim_t RA8875::GetTextWidth(const char * text, bool charOnly)
+{
+    if (font == NULL) {
+        if (charOnly)
+            return fontwidth() * fontScaleX;
+        else
+            return fontwidth() * strlen(text) * fontScaleX;
+    } else {
+        dim_t width = 0;
+
+        while (*text) {
+            dim_t cWidth;
+            if (getCharMetrics(*text, &cWidth, NULL))
+                width += cWidth;
+            if (charOnly)
+                break;
+            text++;
+        }
+        return width * fontScaleX;
+    }
+}
+
 int RA8875::_putc(int c)
 {
     if (font == NULL) {
@@ -1219,8 +1299,8 @@
     }
 }
 
-
-
+// Questions to ponder -
+// - if we choose to wrap to the next line, because the character won't fit on the current line,
 // Questions to ponder -
 // - if we choose to wrap to the next line, because the character won't fit on the current line,
 //      should it erase the space to the width of the screen (in case there is leftover junk there)?
@@ -1237,10 +1317,10 @@
         } else {
             dim_t charWidth, charHeight;
             const uint8_t * charRecord;
-            
+
             charRecord = getCharMetrics(c, &charWidth, &charHeight);
             //int advance = charwidth(c);
-            INFO("(%d,%d) - (%d,%d):(%d,%d), charWidth: %d '%c", cursor_x, cursor_y, 
+            INFO("(%d,%d) - (%d,%d):(%d,%d), charWidth: %d '%c", cursor_x, cursor_y,
                 windowrect.p1.x, windowrect.p1.y, windowrect.p2.x, windowrect.p2.y,
                 charWidth, c);
             if (charRecord) {
@@ -1326,8 +1406,59 @@
     if (font == NULL) {
         WriteCommand(RA8875_MWCR0,0x80);    // Put in Text mode if internal font
     }
-    if (*string != '\0') {
-        while (*string) {           // @TODO calling individual _putc is slower... optimizations?
+    point_t cursor = GetTextCursor();
+    while (*string) {           // @TODO calling individual _putc is slower... optimizations?
+        if (wordwrap) {
+            const char * p = string;
+            const char * pSpace = NULL;
+            dim_t txtPos;
+            bool newLineNeeded = false;
+
+            cursor = GetTextCursor();
+            txtPos = cursor.x;
+            INFO("(%d,%d) string: '%s'\r\n", cursor.x, cursor.y, string);
+            // find what fits in the window
+            do {
+                if (*p == ' ')
+                    pSpace = p;
+                if (*p == '\r') {
+                    pSpace = p;
+                    break;
+                }
+                if (*p == '\n') {
+                    break;
+                }
+                dim_t cWidth = GetTextWidth(p, true);
+                if (txtPos + cWidth < windowrect.p2.x) {
+                    txtPos += cWidth;
+                } else {
+                    newLineNeeded = true;
+                    break;
+                }
+                INFO("+ %c width %d, ttl Width %d\r\n", *p, cWidth, txtPos);
+                p++;
+            } while (*p);
+            INFO(" do { } while();\r\n");
+            if (*p != ' ' && pSpace) {
+                p = pSpace;
+            }
+            INFO("txtPos: %d < windowrect.p2.x: %d\r\n", txtPos, windowrect.p2.x);
+            if (txtPos < windowrect.p2.x) {
+                while (*string && string <= p) {
+                    _putc(*string++);
+                }
+            }
+            while (*string == ' ')
+                string++;
+            if (newLineNeeded) {
+                cursor.y += fontheight();
+                SetTextCursor(cursor);
+                INFO("\r\n");
+            }
+            INFO("### '%s'\r\n\r\n", string);
+            // if != ' ', find ' ', accumulating width along the way
+            // if the width fits, print the chars
+        } else {
             _putc(*string++);
         }
     }
@@ -1349,7 +1480,7 @@
 point_t RA8875::GetGraphicsCursor(void)
 {
     point_t p;
-    
+
     p.x = ReadCommandW(0x46);
     p.y = ReadCommandW(0x48);
     return p;
@@ -1492,10 +1623,10 @@
 // With a font scale Y = 2, a pixel stream is "abcdefg..."
 //                                            "abcdefg..."
 //
-RetCode_t RA8875::booleanStream(loc_t x, loc_t y, dim_t w, dim_t h, const uint8_t * boolStream) 
+RetCode_t RA8875::booleanStream(loc_t x, loc_t y, dim_t w, dim_t h, const uint8_t * boolStream)
 {
     PERFORMANCE_RESET;
-    const uint8_t * rowStream;
+    const uint8_t * rowStream = boolStream;
     rect_t restore = windowrect;
     window(x, y, w * fontScaleX, h * fontScaleY);       // Scale from font scale factors
     SetGraphicsCursor(x, y);
@@ -1506,12 +1637,12 @@
         for (int dy=0; dy<fontScaleY; dy++) {           // Vertical Font Scale Factor
             uint8_t pixels = w;
             uint8_t bitmask = 0x01;
-            rowStream = boolStream;        
+            rowStream = boolStream;
             while (pixels) {
                 uint8_t byte = *rowStream;
                 //INFO("byte, mask: %02X, %02X", byte, bitmask);
                 color_t c = (byte & bitmask) ? _foreground : _background;
-                
+
                 for (int dx=0; dx<fontScaleX; dx++) {   // Horizontal Font Scale Factor
                     if (screenbpp == 16) {
                         _spiwrite(c >> 8);
@@ -1639,13 +1770,17 @@
     } else {
         if (p1.x == p2.x) {
             // vertical
-            fillcircle(p1, thickness/2, color);
-            fillcircle(p2, thickness/2, color);
+            if (roundCap) {
+                fillcircle(p1, thickness / 2, color);
+                fillcircle(p2, thickness / 2, color);
+            }
             fillrect(p1.x-thickness/2,p1.y, p2.x+thickness/2,p2.y, color);
         } else if (p1.y == p2.y) {
             // horizontal
-            fillcircle(p1, thickness/2, color);
-            fillcircle(p2, thickness/2, color);
+            if (roundCap) {
+                fillcircle(p1, thickness / 2, color);
+                fillcircle(p2, thickness / 2, color);
+            }
             fillrect(p1.x,p1.y-thickness/2, p2.x,p2.y+thickness/2, color);
         } else {
             // some diagonal, drawn rather slowly with filled circles
@@ -1653,8 +1788,10 @@
             //      with 2 triangles. 
             #if 1  // New Faster method
             //Round-caps
-            fillcircle(p1, thickness/2, color);
-            fillcircle(p2, thickness/2, color);
+            if (roundCap) {
+                fillcircle(p1, thickness / 2, color);
+                fillcircle(p2, thickness / 2, color);
+            }
             // Compute the perpendicular points to draw the triangles
             //              +           fillTriangle: p1a,p1b,p2a
             //            /   + p1a                   p1a,p2a,p2b
@@ -1673,7 +1810,7 @@
             slope = -1/slope;
             //centerline
             //line(p1,p2,color);
-            float dx = (thickness/2 / sqrt(thickness/2 + (slope * slope))); 
+            float dx = (thickness/2 / sqrt(thickness/2 + (slope * slope)));
             float dy = slope * dx;
             pTri[0].x = p1.x + dx;
             pTri[0].y = p1.y + dy;
@@ -1690,15 +1827,15 @@
             int dx = abs(p2.x-p1.x), sx = p1.x<p2.x ? 1 : -1;
             int dy = abs(p2.y-p1.y), sy = p1.y<p2.y ? 1 : -1;
             int err = (dx>dy ? dx : -dy)/2, e2;
-            
+
             for (;;) {
                 fillcircle(p1.x, p1.y, thickness/2, color);
-                if (p1.x==p2.x && p1.y==p2.y) 
+                if (p1.x==p2.x && p1.y==p2.y)
                     break;
                 e2 = err;
-                if (e2 >-dx) 
+                if (e2 >-dx)
                     { err -= dy; p1.x += sx; }
-                if (e2 < dy) 
+                if (e2 < dy)
                     { err += dx; p1.y += sy; }
             }
             #endif
@@ -1708,6 +1845,13 @@
 }
 
 
+bool RA8875::SetEndCap(bool _roundCap) {
+    bool prevCap = roundCap;
+    roundCap = _roundCap;
+    return prevCap;
+}
+
+
 //
 // Rectangle functions all mostly helpers to the basic rectangle function
 //
@@ -1741,7 +1885,7 @@
     RetCode_t ret = noerror;
     PERFORMANCE_RESET;
     // check for bad_parameter
-    if (x1 < 0 || x1 >= screenwidth || x2 < 0 || x2 >= screenwidth 
+    if (x1 < 0 || x1 >= screenwidth || x2 < 0 || x2 >= screenwidth
     || y1 < 0 || y1 >= screenheight || y2 < 0 || y2 >= screenheight) {
         ret = bad_parameter;
     } else {
@@ -1808,7 +1952,7 @@
 
     INFO("roundrect()");
     PERFORMANCE_RESET;
-    if (x1 < 0 || x1 >= screenwidth || x2 < 0 || x2 >= screenwidth 
+    if (x1 < 0 || x1 >= screenwidth || x2 < 0 || x2 >= screenwidth
     || y1 < 0 || y1 >= screenheight || y2 < 0 || y2 >= screenheight) {
         ret = bad_parameter;
     } else if (x1 > x2 || y1 > y2 || (radius1 > (x2-x1)/2) || (radius2 > (y2-y1)/2) ) {
@@ -1954,7 +2098,7 @@
 RetCode_t RA8875::circle(loc_t x, loc_t y, dim_t radius, fill_t fillit)
 {
     RetCode_t ret = noerror;
-    
+
     INFO("circle");
     PERFORMANCE_RESET;
     #if 0
@@ -2021,7 +2165,7 @@
 
     INFO("ellipse");
     PERFORMANCE_RESET;
-    if ((x - radius1) < 0 || (x + radius1) > screenwidth 
+    if ((x - radius1) < 0 || (x + radius1) > screenwidth
     || (y - radius2) < 0 || (y + radius2) > screenheight) {
         ret = bad_parameter;
     } else if (radius1 == 1 && radius2 == 1) {
@@ -2116,7 +2260,7 @@
 RetCode_t RA8875::Backlight_u8(uint8_t brightness)
 {
     static bool is_enabled = false;
-    
+
     if (brightness == 0) {
         WriteCommand(RA8875_P1CR); // Disable the PWM
         WriteData(0x00);
@@ -2165,7 +2309,7 @@
         uint16_t firstChar = _font[3] * 256 + _font[2];
         uint16_t lastChar  = _font[5] * 256 + _font[4];
         uint16_t i;
-        
+
         for (i=firstChar; i<=lastChar; i++) {
             // 8 bytes of preamble to the first level lookup table
             uint16_t offsetToCharLookup = 8 + 4 * (i - firstChar);    // 4-bytes: width(pixels), 16-bit offset from table start, 0
@@ -2279,7 +2423,7 @@
 RetCode_t RA8875::PrintScreen(uint16_t layer, loc_t x, loc_t y, dim_t w, dim_t h, const char *Name_BMP)
 {
     (void)layer;
-    
+
     // AttachPrintHandler(this, RA8875::_printCallback);
     // return PrintScreen(x,y,w,h);
     return PrintScreen(x, y, w, h, Name_BMP);
@@ -2314,7 +2458,7 @@
 
 int RA8875::RoundUp(int value, int roundTo)
 {
-    if (roundTo == 0) 
+    if (roundTo == 0)
         return 0;
     return ((value + roundTo - 1) / roundTo) * roundTo;
 }
@@ -2326,7 +2470,7 @@
     uint8_t * lineBuffer = NULL;
     color_t * pixelBuffer = NULL;
     color_t * pixelBuffer2 = NULL;
-    
+
     INFO("(%d,%d)-(%d,%d)x%d", x,y,w,h,bitsPerPixel);
     if (x >= 0 && x < screenwidth
             && y >= 0 && y < screenheight
@@ -2367,7 +2511,7 @@
         BMP_Info.biYPelsPerMeter = 0;
         // for 24-bit, there is no palette, so these are zero
         // for 8-bit, there can be up to 256 RGB values in the palette
-        
+
         BMP_Info.biClrUsed = (bitsPerPixel == 24) ? 0 : sizeof(WebColorPalette)/sizeof(WebColorPalette[0]);    // for 8b/pixel
         BMP_Info.biClrImportant = BMP_Info.biClrUsed;
 
@@ -2380,7 +2524,7 @@
         memset(lineBuffer, 0, lineBufSize); // zero-Fill
 
         #define DOUBLEBUF /* one larger buffer instead of two */
-        
+
         #ifdef DOUBLEBUF
         // In the "#else", pixelBuffer2 malloc returns a value, 
         // but is actually causing a failure later. 
@@ -2540,7 +2684,7 @@
     uint8_t * lineBuffer = NULL;
     color_t * pixelBuffer = NULL;
     color_t * pixelBuffer2 = NULL;
-    
+
     INFO("(%d,%d)-(%d,%d)x%d %s", x,y,w,h,bitsPerPixel,Name_BMP);
     if (x >= 0 && x < screenwidth
             && y >= 0 && y < screenheight
@@ -2581,7 +2725,7 @@
         BMP_Info.biYPelsPerMeter = 0;
         // for 24-bit, there is no palette, so these are zero
         // for 8-bit, there can be up to 256 RGB values in the palette
-        
+
         BMP_Info.biClrUsed = (bitsPerPixel == 24) ? 0 : sizeof(WebColorPalette)/sizeof(WebColorPalette[0]);    // for 8b/pixel
         BMP_Info.biClrImportant = BMP_Info.biClrUsed;
 
@@ -2594,7 +2738,7 @@
         memset(lineBuffer, 0, lineBufSize); // zero-Fill
 
         #define DOUBLEBUF /* one larger buffer instead of two */
-        
+
         #ifdef DOUBLEBUF
         // In the "#else", pixelBuffer2 malloc returns a value, 
         // but is actually causing a failure later. 
@@ -2640,13 +2784,13 @@
 
         HexDump("BMP_Info", (uint8_t *)&BMP_Info, sizeof(BMP_Info));
         fwrite(&BMP_Info, sizeof(char), sizeof(BMP_Info), Image);
-        
+
         if (bitsPerPixel != 24) {
             HexDump("Palette", (uint8_t *)&WebColorPalette, sizeof(WebColorPalette));
             fwrite(&WebColorPalette, sizeof(char), sizeof(WebColorPalette), Image);
             if (0 && sizeof(WebColorPalette) % 4) {
                 const uint8_t padd[] = { 0, 0, 0 };
-                fwrite(&padd, sizeof(char), 
+                fwrite(&padd, sizeof(char),
                     (sizeof(BMP_Header) + sizeof(BMP_Info) + sizeof(WebColorPalette)) % 4, Image);
             }
         }
@@ -2995,7 +3139,7 @@
 
     display.SelectUserFont(BPG_Arial20x20);
     display.puts("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\r\n");
-    
+
     display.SelectUserFont();
 
     display.puts("Normal font again.");
@@ -3381,7 +3525,7 @@
     Timer t;
     int x, y;
     tpMatrix_t calmatrix;
-    
+
     display.background(Black);
     display.foreground(Blue);
     display.cls();