/* ILI9341_due_.cpp - Arduino Due library for interfacing with ILI9341-based TFTs Copyright (c) 2014 Marek Buriak Modified 03/19/2015 by Victor Perez to add STM32F1 Maple mini DMA support This library is based on ILI9341_t3 library from Paul Stoffregen (https://github.com/PaulStoffregen/ILI9341_t3), Adafruit_ILI9341 and Adafruit_GFX libraries from Limor Fried/Ladyada (https://github.com/adafruit/Adafruit_ILI9341). This file is part of the Arduino ILI9341_due library. Sources for this library can be found at https://github.com/marekburiak/ILI9341_Due. ILI9341_due is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the License, or (at your option) any later version. ILI9341_due is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with ILI9341_due. If not, see . */ /*************************************************** This is our library for the Adafruit ILI9341 Breakout and Shield ----> http://www.adafruit.com/products/1651 Check out the links above for our tutorials and wiring diagrams These displays use SPI to communicate, 4 or 5 pins are required to interface (RST is optional) Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! Written by Limor Fried/Ladyada for Adafruit Industries. MIT license, all text above must be included in any redistribution ****************************************************/ #include "ILI9341_due.h" #if SPI_MODE_NORMAL | SPI_MODE_EXTENDED #include #elif SPI_MODE_DMA #include "ILI_SdSpi.h" #include #endif //#include "..\Streaming\Streaming.h" static const uint8_t init_commands[] PROGMEM = { 4, 0xEF, 0x03, 0x80, 0x02, 4, 0xCF, 0x00, 0XC1, 0X30, 5, 0xED, 0x64, 0x03, 0X12, 0X81, 4, 0xE8, 0x85, 0x00, 0x78, 6, 0xCB, 0x39, 0x2C, 0x00, 0x34, 0x02, 2, 0xF7, 0x20, 3, 0xEA, 0x00, 0x00, 2, ILI9341_PWCTR1, 0x23, // Power control 2, ILI9341_PWCTR2, 0x10, // Power control 3, ILI9341_VMCTR1, 0x3e, 0x28, // VCM control 2, ILI9341_VMCTR2, 0x86, // VCM control2 2, ILI9341_MADCTL, 0x48, // Memory Access Control 2, ILI9341_PIXFMT, 0x55, 3, ILI9341_FRMCTR1, 0x00, 0x18, 4, ILI9341_DFUNCTR, 0x08, 0x82, 0x27, // Display Function Control 2, 0xF2, 0x00, // Gamma Function Disable 2, ILI9341_GAMMASET, 0x01, // Gamma curve selected 16, ILI9341_GMCTRP1, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00, // Set Gamma 16, ILI9341_GMCTRN1, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, // Set Gamma 0 }; ILI9341_due::ILI9341_due(uint8_t cs, uint8_t dc, uint8_t rst) { _cs = cs; _dc = dc; _rst = rst; _width = ILI9341_TFTWIDTH; _height = ILI9341_TFTHEIGHT; _rotation = iliRotation0; #ifdef FEATURE_PRINT_ENABLED _cursorY = _cursorX = 0; _textsize = 1; _textcolor = _textbgcolor = 0xFFFF; _wrap = true; #endif #ifdef FEATURE_ARC_ENABLED _arcAngleMax = ARC_ANGLE_MAX; _arcAngleOffset = ARC_ANGLE_OFFSET; #endif } bool ILI9341_due::begin(void) { if (pinIsChipSelect(_cs)) { pinMode(_dc, OUTPUT); _dcport = portOutputRegister(digitalPinToPort(_dc)); _dcpinmask = digitalPinToBitMask(_dc); #if SPI_MODE_NORMAL | SPI_MODE_DMA pinMode(_cs, OUTPUT); _csport = portOutputRegister(digitalPinToPort(_cs)); _cspinmask = digitalPinToBitMask(_cs); #endif #if SPI_MODE_NORMAL SPI.begin(); SPI.setClockDivider(ILI9341_SPI_CLKDIVIDER); SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); #ifdef __AVR__ _SPCR = SPCR; #endif #elif SPI_MODE_EXTENDED SPI.begin(_cs); SPI.setClockDivider(_cs, ILI9341_SPI_CLKDIVIDER); SPI.setBitOrder(_cs, MSBFIRST); SPI.setDataMode(_cs, SPI_MODE0); #elif SPI_MODE_DMA _dmaSpi.begin(); _dmaSpi.init(ILI9341_SPI_CLKDIVIDER); #endif // toggle RST low to reset if (_rst < 255) { pinMode(_rst, OUTPUT); digitalWrite(_rst, HIGH); delay(5); digitalWrite(_rst, LOW); delay(20); digitalWrite(_rst, HIGH); delay(150); } const uint8_t *addr = init_commands; while (1) { uint8_t count = pgm_read_byte(addr++); if (count-- == 0) break; writecommand_cont(pgm_read_byte(addr++)); while (count-- > 0) { writedata8_cont(pgm_read_byte(addr++)); } } writecommand_last(ILI9341_SLPOUT); // Exit Sleep delay(120); writecommand_last(ILI9341_DISPON); // Display on delay(120); _isInSleep = _isIdle = false; uint8_t x = readcommand8(ILI9341_RDMODE); Serial.print(F("\nDisplay Power Mode: 0x")); Serial.println(x, HEX); x = readcommand8(ILI9341_RDMADCTL); Serial.print(F("\nMADCTL Mode: 0x")); Serial.println(x, HEX); x = readcommand8(ILI9341_RDPIXFMT); Serial.print(F("\nPixel Format: 0x")); Serial.println(x, HEX); x = readcommand8(ILI9341_RDIMGFMT); Serial.print(F("\nImage Format: 0x")); Serial.println(x, HEX); x = readcommand8(ILI9341_RDSELFDIAG); Serial.print(F("\nSelf Diagnostic: 0x")); Serial.println(x, HEX); return true; } else { return false; } } bool ILI9341_due::pinIsChipSelect(uint8_t cs) { #if SPI_MODE_EXTENDED if(cs == 4 || cs == 10 || cs == 52) // in Extended SPI mode only these pins are valid { return true; } else { Serial.print("Pin "); Serial.print(_cs); Serial.println(" is not a valid Chip Select pin for SPI Extended Mode. Valid pins are 4, 10, 52"); return false; } #elif SPI_MODE_NORMAL | SPI_MODE_DMA return true; #endif } void ILI9341_due::setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { setAddrAndRW_cont(x0, y0, x1, y1); disableCS(); } void ILI9341_due::setSPIClockDivider(uint8_t divider) { #if SPI_MODE_NORMAL SPI.setClockDivider(divider); #elif SPI_MODE_EXTENDED SPI.setClockDivider(_cs, divider); #elif SPI_MODE_DMA _dmaSpi.init(divider); #endif } void ILI9341_due::pushColor(uint16_t color) { enableCS(); writedata16_last(color); } void ILI9341_due::pushColors(uint16_t *colors, uint16_t offset, uint16_t len) { enableCS(); setDCForData(); colors = colors + offset * 2; #if SPI_MODE_NORMAL | SPI_MODE_EXTENDED for (uint16_t i = 0; i < len; i++) { write16_cont(colors[i]); } #elif SPI_MODE_DMA for (uint16_t i = 0; i < (len << 1); i += 2) { uint16_t color = *colors; _scanlineBuffer[i] = highByte(color); _scanlineBuffer[i + 1] = lowByte(color); colors++; } writeScanline_cont(len); #endif disableCS(); } // pushes pixels stored in the colors array (one color is 2 bytes) // in big endian (high byte first) // len should be the length of the array (so to push 320 pixels, // you have to have a 640-byte array and len should be 640) void ILI9341_due::pushColors565(uint8_t *colors, uint16_t offset, uint16_t len) { enableCS(); setDCForData(); colors = colors + offset; #if SPI_MODE_NORMAL | SPI_MODE_EXTENDED for (uint16_t i = 0; i < len; i++) { write8_cont(colors[i]); } #elif SPI_MODE_DMA write_cont(colors, len); #endif disableCS(); } void ILI9341_due::drawPixel(int16_t x, int16_t y, uint16_t color) { if ((x < 0) || (x >= _width) || (y < 0) || (y >= _height)) return; writePixel_last(x, y, color); } void ILI9341_due::drawPixel_cont(int16_t x, int16_t y, uint16_t color) { if ((x < 0) || (x >= _width) || (y < 0) || (y >= _height)) return; writePixel_cont(x, y, color); } void ILI9341_due::drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) { // Rudimentary clipping if ((x >= _width) || (y >= _height || h == 0)) return; if ((y + h - 1) >= _height) h = _height - y; setAddrAndRW_cont(x, y, x, y + h - 1); #if SPI_MODE_NORMAL | SPI_MODE_EXTENDED setDCForData(); while (h-- > 1) { write16_cont(color); } write16_last(color); #elif SPI_MODE_DMA fillScanline(color, h); writeScanline_last(h); #endif } void ILI9341_due::drawFastVLine_cont_noFill(int16_t x, int16_t y, int16_t h, uint16_t color) { // Rudimentary clipping if ((x >= _width) || (y >= _height || h == 0)) return; if ((y + h - 1) >= _height) h = _height - y; setAddrAndRW_cont(x, y, x, y + h - 1); #if SPI_MODE_NORMAL | SPI_MODE_EXTENDED setDCForData(); while (h-- > 0) { write16_cont(color); } #elif SPI_MODE_DMA writeScanline_cont(h); #endif } void ILI9341_due::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) { // Rudimentary clipping if ((x >= _width) || (y >= _height || w == 0)) return; if ((x + w - 1) >= _width) w = _width - x; setAddrAndRW_cont(x, y, x + w - 1, y); #if SPI_MODE_NORMAL | SPI_MODE_EXTENDED setDCForData(); while (w-- > 1) { write16_cont(color); } writedata16_last(color); #elif SPI_MODE_DMA fillScanline(color, w); writeScanline_last(w); #endif } void ILI9341_due::fillScreen(uint16_t color) { #if SPI_MODE_NORMAL | SPI_MODE_EXTENDED fillRect(0, 0, _width, _height, color); #elif SPI_MODE_DMA fillScanline(color); setAddrAndRW_cont(0, 0, _width - 1, _height - 1); setDCForData(); const uint16_t bytesToWrite = _width << 1; for (uint16_t y = 0; y < _height; y++) { write_cont(_scanlineBuffer, bytesToWrite); } disableCS(); #endif } // fill a rectangle void ILI9341_due::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) { //Serial << "x:" << x << " y:" << y << " w:" << x << " h:" << h << " width:" << _width << " height:" << _height <= _width) || (y >= _height || w == 0 || h == 0)) return; if ((x + w - 1) >= _width) w = _width - x; if ((y + h - 1) >= _height) h = _height - y; setAddrAndRW_cont(x, y, x + w - 1, y + h - 1); #if SPI_MODE_DMA const uint16_t maxNumLinesInScanlineBuffer = (SCANLINE_BUFFER_SIZE >> 1) / w; const uint16_t numPixelsInOneGo = w*maxNumLinesInScanlineBuffer; fillScanline(color, numPixelsInOneGo); for (uint16_t p = 0; p < h / maxNumLinesInScanlineBuffer; p++) { writeScanline_cont(numPixelsInOneGo); } writeScanline_last((w*h) % numPixelsInOneGo); #elif SPI_MODE_NORMAL | SPI_MODE_EXTENDED // TODO: this can result in a very long transaction time // should break this into multiple transactions, even though // it'll cost more overhead, so we don't stall other SPI libs //enableCS(); //setAddrAndRW_cont will enable CS setDCForData(); y=h; while(y--) { x=w; while(x--) { write16_cont(color); } } disableCS(); #endif } #define MADCTL_MY 0x80 #define MADCTL_MX 0x40 #define MADCTL_MV 0x20 #define MADCTL_ML 0x10 #define MADCTL_RGB 0x00 #define MADCTL_BGR 0x08 #define MADCTL_MH 0x04 void ILI9341_due::setRotation(iliRotation r) { writecommand_cont(ILI9341_MADCTL); _rotation = r; switch (r) { case iliRotation0: writedata8_last(MADCTL_MX | MADCTL_BGR); _width = ILI9341_TFTWIDTH; _height = ILI9341_TFTHEIGHT; break; case iliRotation90: writedata8_last(MADCTL_MV | MADCTL_BGR); _width = ILI9341_TFTHEIGHT; _height = ILI9341_TFTWIDTH; break; case iliRotation180: writedata8_last(MADCTL_MY | MADCTL_BGR); _width = ILI9341_TFTWIDTH; _height = ILI9341_TFTHEIGHT; break; case iliRotation270: writedata8_last(MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_BGR); _width = ILI9341_TFTHEIGHT; _height = ILI9341_TFTWIDTH; break; } } void ILI9341_due::invertDisplay(boolean i) { writecommand_last(i ? ILI9341_INVON : ILI9341_INVOFF); } uint8_t ILI9341_due::readcommand8(uint8_t c, uint8_t index) { writecommand_cont(0xD9); // woo sekret command? writedata8_last(0x10 + index); writecommand_cont(c); return readdata8_last(); } // Reads one pixel/color from the TFT's GRAM uint16_t ILI9341_due::readPixel(int16_t x, int16_t y) { setAddr_cont(x, y, x + 1, y + 1); writecommand_cont(ILI9341_RAMRD); // read from RAM readdata8_cont(); // dummy read uint8_t red = read8_cont(); uint8_t green = read8_cont(); uint8_t blue = read8_last(); return color565(red, green, blue); } //void ILI9341_due::drawArc(uint16_t cx, uint16_t cy, uint16_t radius, uint16_t thickness, uint16_t start, uint16_t end, uint16_t color) { // //void graphics_draw_arc(GContext *ctx, GPoint p, int radius, int thickness, int start, int end) { // start = start % 360; // end = end % 360; // // while (start < 0) start += 360; // while (end < 0) end += 360; // // if (end == 0) end = 360; // // //Serial << "start: " << start << " end:" << end << endl; // // // Serial << (float)cos_lookup(start * ARC_MAX_STEPS / 360) << " x " << (float)sin_lookup(start * ARC_MAX_STEPS / 360) << endl; // // float sslope = (float)cos_lookup(start * ARC_MAX_STEPS / 360) / (float)sin_lookup(start * ARC_MAX_STEPS / 360); // float eslope = (float)cos_lookup(end * ARC_MAX_STEPS / 360) / (float)sin_lookup(end * ARC_MAX_STEPS / 360); // // //Serial << "sslope: " << sslope << " eslope:" << eslope << endl; // // if (end == 360) eslope = -1000000; // // int ir2 = (radius - thickness) * (radius - thickness); // int or2 = radius * radius; // // for (int x = -radius; x <= radius; x++) // for (int y = -radius; y <= radius; y++) // { // int x2 = x * x; // int y2 = y * y; // // if ( // (x2 + y2 < or2 && x2 + y2 >= ir2) && // ( // (y > 0 && start < 180 && x <= y * sslope) || // (y < 0 && start > 180 && x >= y * sslope) || // (y < 0 && start <= 180) || // (y == 0 && start <= 180 && x < 0) || // (y == 0 && start == 0 && x > 0) // ) && // ( // (y > 0 && end < 180 && x >= y * eslope) || // (y < 0 && end > 180 && x <= y * eslope) || // (y > 0 && end >= 180) || // (y == 0 && end >= 180 && x < 0) || // (y == 0 && start == 0 && x > 0) // ) // ) // drawPixel_cont(cx+x, cy+y, color); // } //} #ifdef FEATURE_ARC_ENABLED // DrawArc function thanks to Jnmattern and his Arc_2.0 (https://github.com/Jnmattern) void ILI9341_due::drawArcOffsetted(uint16_t cx, uint16_t cy, uint16_t radius, uint16_t thickness, float start, float end, uint16_t color) { int16_t xmin = 65535, xmax = -32767, ymin = 32767, ymax = -32767; float cosStart, sinStart, cosEnd, sinEnd; float r, t; float startAngle, endAngle; //Serial << "start: " << start << " end: " << end << endl; startAngle = (start / _arcAngleMax) * 360; // 252 endAngle = (end / _arcAngleMax) * 360; // 807 //Serial << "startAngle: " << startAngle << " endAngle: " << endAngle << endl; while (startAngle < 0) startAngle += 360; while (endAngle < 0) endAngle += 360; while (startAngle > 360) startAngle -= 360; while (endAngle > 360) endAngle -= 360; //Serial << "startAngleAdj: " << startAngle << " endAngleAdj: " << endAngle << endl; //if (endAngle == 0) endAngle = 360; if (startAngle > endAngle) { drawArcOffsetted(cx, cy, radius, thickness, ((startAngle) / (float)360) * _arcAngleMax, _arcAngleMax, color); drawArcOffsetted(cx, cy, radius, thickness, 0, ((endAngle) / (float)360) * _arcAngleMax, color); } else { // Calculate bounding box for the arc to be drawn cosStart = cosDegrees(startAngle); sinStart = sinDegrees(startAngle); cosEnd = cosDegrees(endAngle); sinEnd = sinDegrees(endAngle); //Serial << cosStart << " " << sinStart << " " << cosEnd << " " << sinEnd << endl; r = radius; // Point 1: radius & startAngle t = r * cosStart; if (t < xmin) xmin = t; if (t > xmax) xmax = t; t = r * sinStart; if (t < ymin) ymin = t; if (t > ymax) ymax = t; // Point 2: radius & endAngle t = r * cosEnd; if (t < xmin) xmin = t; if (t > xmax) xmax = t; t = r * sinEnd; if (t < ymin) ymin = t; if (t > ymax) ymax = t; r = radius - thickness; // Point 3: radius-thickness & startAngle t = r * cosStart; if (t < xmin) xmin = t; if (t > xmax) xmax = t; t = r * sinStart; if (t < ymin) ymin = t; if (t > ymax) ymax = t; // Point 4: radius-thickness & endAngle t = r * cosEnd; if (t < xmin) xmin = t; if (t > xmax) xmax = t; t = r * sinEnd; if (t < ymin) ymin = t; if (t > ymax) ymax = t; //Serial << xmin << " " << xmax << " " << ymin << " " << ymax << endl; // Corrections if arc crosses X or Y axis if ((startAngle < 90) && (endAngle > 90)) { ymax = radius; } if ((startAngle < 180) && (endAngle > 180)) { xmin = -radius; } if ((startAngle < 270) && (endAngle > 270)) { ymin = -radius; } // Slopes for the two sides of the arc float sslope = (float)cosStart / (float)sinStart; float eslope = (float)cosEnd / (float)sinEnd; //Serial << "sslope2: " << sslope << " eslope2:" << eslope << endl; if (endAngle == 360) eslope = -1000000; int ir2 = (radius - thickness) * (radius - thickness); int or2 = radius * radius; //Serial << "ymin: " << ymin << " ymax: " << ymax << endl; #if SPI_MODE_DMA fillScanline(color, radius << 1); #endif for (int x = xmin; x <= xmax; x++) { bool y1StartFound = false, y2StartFound = false; bool y1EndFound = false, y2EndSearching = false; int y1s = 0, y1e = 0, y2s = 0, y2e = 0; for (int y = ymin; y <= ymax; y++) { int x2 = x * x; int y2 = y * y; if ( (x2 + y2 < or2 && x2 + y2 >= ir2) && ( (y > 0 && startAngle < 180 && x <= y * sslope) || (y < 0 && startAngle > 180 && x >= y * sslope) || (y < 0 && startAngle <= 180) || (y == 0 && startAngle <= 180 && x < 0) || (y == 0 && startAngle == 0 && x > 0) ) && ( (y > 0 && endAngle < 180 && x >= y * eslope) || (y < 0 && endAngle > 180 && x <= y * eslope) || (y > 0 && endAngle >= 180) || (y == 0 && endAngle >= 180 && x < 0) || (y == 0 && startAngle == 0 && x > 0))) { if (!y1StartFound) //start of the higher line found { y1StartFound = true; y1s = y; } else if (y1EndFound && !y2StartFound) //start of the lower line found { //Serial << "Found y2 start x: " << x << " y:" << y << endl; y2StartFound = true; //drawPixel_cont(cx+x, cy+y, ILI9341_BLUE); y2s = y; y += y1e - y1s - 1; // calculate the most probable end of the lower line (in most cases the length of lower line is equal to length of upper line), in the next loop we will validate if the end of line is really there if (y > ymax - 1) // the most probable end of line 2 is beyond ymax so line 2 must be shorter, thus continue with pixel by pixel search { y = y2s; // reset y and continue with pixel by pixel search y2EndSearching = true; } //Serial << "Upper line length: " << (y1e - y1s) << " Setting y to " << y << endl; } else if (y2StartFound && !y2EndSearching) { // we validated that the probable end of the lower line has a pixel, continue with pixel by pixel search, in most cases next loop with confirm the end of lower line as it will not find a valid pixel y2EndSearching = true; } //Serial << "x:" << x << " y:" << y << endl; //drawPixel_cont(cx+x, cy+y, ILI9341_BLUE); } else { if (y1StartFound && !y1EndFound) //higher line end found { y1EndFound = true; y1e = y - 1; //Serial << "line: " << y1s << " - " << y1e << endl; drawFastVLine_cont_noFill(cx + x, cy + y1s, y - y1s, color); if (y < 0) { //Serial << x << " " << y << endl; y = abs(y); // skip the empty middle } else break; } else if (y2StartFound) { if (y2EndSearching) { //Serial << "Found final end at y: " << y << endl; // we found the end of the lower line after pixel by pixel search drawFastVLine_cont_noFill(cx + x, cy + y2s, y - y2s, color); y2EndSearching = false; break; } else { //Serial << "Expected end not found" << endl; // the expected end of the lower line is not there so the lower line must be shorter y = y2s; // put the y back to the lower line start and go pixel by pixel to find the end y2EndSearching = true; } } //else //drawPixel_cont(cx+x, cy+y, ILI9341_RED); } // //delay(75); } if (y1StartFound && !y1EndFound) { y1e = ymax; //Serial << "line: " << y1s << " - " << y1e << endl; drawFastVLine_cont_noFill(cx + x, cy + y1s, y1e - y1s + 1, color); } else if (y2StartFound && y2EndSearching) // we found start of lower line but we are still searching for the end { // which we haven't found in the loop so the last pixel in a column must be the end drawFastVLine_cont_noFill(cx + x, cy + y2s, ymax - y2s + 1, color); } } disableCS(); } } #endif void ILI9341_due::screenshotToConsole() { uint16_t color565; uint8_t lastColor[3]; uint8_t color[3]; uint32_t sameColorPixelCount = 0; uint16_t sameColorPixelCount16 = 0; uint32_t sameColorStartIndex = 0; uint32_t totalImageDataLength = 0; Serial.println(F("==== PIXEL DATA START ====")); //uint16_t x=0; //uint16_t y=0; setAddr_cont(0, 0, _width - 1, _height - 1); writecommand_cont(ILI9341_RAMRD); // read from RAM readdata8_cont(); // dummy read, also sets DC high #if SPI_MODE_DMA read_cont(color, 3); lastColor[0] = color[0]; lastColor[1] = color[1]; lastColor[2] = color[2]; #elif SPI_MODE_NORMAL | SPI_MODE_EXTENDED lastColor[0] = color[0] = read8_cont(); lastColor[1] = color[1] = read8_cont(); lastColor[2] = color[2] = read8_cont(); #endif printHex8(color, 3); //write color of the first pixel totalImageDataLength += 6; sameColorStartIndex = 0; for (uint32_t i = 1; i < _width*_height; i++) { #if SPI_MODE_DMA read_cont(color, 3); #elif SPI_MODE_NORMAL | SPI_MODE_EXTENDED color[0] = read8_cont(); color[1] = read8_cont(); color[2] = read8_cont(); #endif if (color[0] != lastColor[0] || color[1] != lastColor[1] || color[2] != lastColor[2]) { sameColorPixelCount = i - sameColorStartIndex; if (sameColorPixelCount > 65535) { sameColorPixelCount16 = 65535; printHex16(&sameColorPixelCount16, 1); printHex8(lastColor, 3); totalImageDataLength += 10; sameColorPixelCount16 = sameColorPixelCount - 65535; } else sameColorPixelCount16 = sameColorPixelCount; printHex16(&sameColorPixelCount16, 1); printHex8(color, 3); totalImageDataLength += 10; sameColorStartIndex = i; lastColor[0] = color[0]; lastColor[1] = color[1]; lastColor[2] = color[2]; } } sameColorPixelCount = _width*_height - sameColorStartIndex; if (sameColorPixelCount > 65535) { sameColorPixelCount16 = 65535; printHex16(&sameColorPixelCount16, 1); printHex8(lastColor, 3); totalImageDataLength += 10; sameColorPixelCount16 = sameColorPixelCount - 65535; } else sameColorPixelCount16 = sameColorPixelCount; printHex16(&sameColorPixelCount16, 1); totalImageDataLength += 4; printHex32(&totalImageDataLength, 1); /*for(uint16_t x=0; x<_width; x++) { for(uint16_t y=0; y<_height; y++) { color[0] = read8(); color[1] = read8(); color[2] = read8(); if(color[0] != lastColor[0] || color[1] != lastColor[1] || color[2] != lastColor[2]) { } PrintHex8(color, 3); } }*/ Serial.println(); Serial.println(F("==== PIXEL DATA END ====")); Serial.print(F("Total Image Data Length: ")); Serial.println(totalImageDataLength); #if SPI_MODE_DMA _dmaSpi.init(ILI9341_SPI_CLKDIVIDER); #endif disableCS(); } /* This is the core graphics library for all our displays, providing a common set of graphics primitives (points, lines, circles, etc.). It needs to bex paired with a hardware-specific library for each display device we carry (to handle the lower-level functions). Adafruit invests time and resources providing this open source code, please support Adafruit & open-source hardware by purchasing products from Adafruit! Copyright (c) 2013 Adafruit Industries. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "glcdfont.c" // Draw a circle outline void ILI9341_due::drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color) { int16_t f = 1 - r; int16_t ddF_x = 1; int16_t ddF_y = -2 * r; int16_t x = 0; int16_t y = r; drawPixel_cont(x0, y0 + r, color); drawPixel_cont(x0, y0 - r, color); drawPixel_cont(x0 + r, y0, color); drawPixel_cont(x0 - r, y0, color); while (x < y) { if (f >= 0) { y--; ddF_y += 2; f += ddF_y; } x++; ddF_x += 2; f += ddF_x; drawPixel_cont(x0 + x, y0 + y, color); drawPixel_cont(x0 - x, y0 + y, color); drawPixel_cont(x0 + x, y0 - y, color); drawPixel_cont(x0 - x, y0 - y, color); drawPixel_cont(x0 + y, y0 + x, color); drawPixel_cont(x0 - y, y0 + x, color); drawPixel_cont(x0 + y, y0 - x, color); drawPixel_cont(x0 - y, y0 - x, color); } disableCS(); } void ILI9341_due::drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, uint16_t color) { int16_t f = 1 - r; int16_t ddF_x = 1; int16_t ddF_y = -2 * r; int16_t x = 0; int16_t y = r; while (x < y) { if (f >= 0) { y--; ddF_y += 2; f += ddF_y; } x++; ddF_x += 2; f += ddF_x; if (cornername & 0x4) { drawPixel_cont(x0 + x, y0 + y, color); drawPixel_cont(x0 + y, y0 + x, color); } if (cornername & 0x2) { drawPixel_cont(x0 + x, y0 - y, color); drawPixel_cont(x0 + y, y0 - x, color); } if (cornername & 0x8) { drawPixel_cont(x0 - y, y0 + x, color); drawPixel_cont(x0 - x, y0 + y, color); } if (cornername & 0x1) { drawPixel_cont(x0 - y, y0 - x, color); drawPixel_cont(x0 - x, y0 - y, color); } } disableCS(); } void ILI9341_due::fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color) { drawFastVLine(x0, y0 - r, 2 * r + 1, color); fillCircleHelper(x0, y0, r, 3, 0, color); } // Used to do circles and roundrects void ILI9341_due::fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, uint16_t color) { int16_t f = 1 - r; int16_t ddF_x = 1; int16_t ddF_y = -2 * r; int16_t x = 0; int16_t y = r; #if SPI_MODE_DMA fillScanline(color, 2 * max(x, y) + 1 + delta); #endif enableCS(); while (x < y) { if (f >= 0) { y--; ddF_y += 2; f += ddF_y; } x++; ddF_x += 2; f += ddF_x; if (cornername & 0x1) { drawFastVLine_cont_noFill(x0 + x, y0 - y, 2 * y + 1 + delta, color); drawFastVLine_cont_noFill(x0 + y, y0 - x, 2 * x + 1 + delta, color); } if (cornername & 0x2) { drawFastVLine_cont_noFill(x0 - x, y0 - y, 2 * y + 1 + delta, color); drawFastVLine_cont_noFill(x0 - y, y0 - x, 2 * x + 1 + delta, color); } } disableCS(); } // Bresenham's algorithm - thx wikpedia void ILI9341_due::drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color) { if (y0 == y1) { if (x1 > x0) { drawFastHLine(x0, y0, x1 - x0 + 1, color); } else if (x1 < x0) { drawFastHLine(x1, y0, x0 - x1 + 1, color); } else { drawPixel(x0, y0, color); } return; } else if (x0 == x1) { if (y1 > y0) { drawFastVLine(x0, y0, y1 - y0 + 1, color); } else { drawFastVLine(x0, y1, y0 - y1 + 1, color); } return; } bool steep = abs(y1 - y0) > abs(x1 - x0); if (steep) { swap(x0, y0); swap(x1, y1); } if (x0 > x1) { swap(x0, x1); swap(y0, y1); } int16_t dx, dy; dx = x1 - x0; dy = abs(y1 - y0); int16_t err = dx / 2; int16_t ystep; if (y0 < y1) { ystep = 1; } else { ystep = -1; } int16_t xbegin = x0; #if SPI_MODE_DMA fillScanline(color); #endif enableCS(); if (steep) { for (; x0 <= x1; x0++) { err -= dy; if (err < 0) { int16_t len = x0 - xbegin; if (len) { writeVLine_cont_noCS_noFill(y0, xbegin, len + 1, color); } else { writePixel_cont_noCS(y0, x0, color); } xbegin = x0 + 1; y0 += ystep; err += dx; } } if (x0 > xbegin + 1) { writeVLine_cont_noCS_noFill(y0, xbegin, x0 - xbegin, color); } } else { for (; x0 <= x1; x0++) { err -= dy; if (err < 0) { int16_t len = x0 - xbegin; if (len) { writeHLine_cont_noCS_noFill(xbegin, y0, len + 1, color); } else { writePixel_cont_noCS(x0, y0, color); } xbegin = x0 + 1; y0 += ystep; err += dx; } } if (x0 > xbegin + 1) { writeHLine_cont_noCS_noFill(xbegin, y0, x0 - xbegin, color); } } disableCS(); } // Draw a rectangle //void ILI9341_due::drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) //{ // writeHLine_cont(x, y, w, color); // writeHLine_cont(x, y+h-1, w, color); // writeVLine_cont(x, y, h, color); // writeVLine_last(x+w-1, y, h, color); //} void ILI9341_due::drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) { #if SPI_MODE_DMA fillScanline(color, max(w, h)); #endif enableCS(); writeHLine_cont_noCS_noFill(x, y, w, color); writeHLine_cont_noCS_noFill(x, y + h - 1, w, color); writeVLine_cont_noCS_noFill(x, y, h, color); writeVLine_cont_noCS_noFill(x + w - 1, y, h, color); disableCS(); } // Draw a rounded rectangle void ILI9341_due::drawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color) { if ( h <= 2 * r || w <= 2 * r) return; #if SPI_MODE_DMA fillScanline(color, max(w, h)); #endif enableCS(); // smarter version writeHLine_cont_noCS_noFill(x + r, y, w - 2 * r, color); // Top writeHLine_cont_noCS_noFill(x + r, y + h - 1, w - 2 * r, color); // Bottom writeVLine_cont_noCS_noFill(x, y + r, h - 2 * r, color); // Left writeVLine_cont_noCS_noFill(x + w - 1, y + r, h - 2 * r, color); // Right disableCS(); // draw four corners drawCircleHelper(x + r, y + r, r, 1, color); drawCircleHelper(x + w - r - 1, y + r, r, 2, color); drawCircleHelper(x + w - r - 1, y + h - r - 1, r, 4, color); drawCircleHelper(x + r, y + h - r - 1, r, 8, color); } // Fill a rounded rectangle void ILI9341_due::fillRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color) { // smarter version fillRect(x + r, y, w - 2 * r, h, color); // draw four corners fillCircleHelper(x + w - r - 1, y + r, r, 1, h - 2 * r - 1, color); fillCircleHelper(x + r, y + r, r, 2, h - 2 * r - 1, color); } // Draw a triangle void ILI9341_due::drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color) { drawLine(x0, y0, x1, y1, color); drawLine(x1, y1, x2, y2, color); drawLine(x2, y2, x0, y0, color); } // Fill a triangle void ILI9341_due::fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color) { int16_t a, b, y, last; // Sort coordinates by Y order (y2 >= y1 >= y0) if (y0 > y1) { swap(y0, y1); swap(x0, x1); } if (y1 > y2) { swap(y2, y1); swap(x2, x1); } if (y0 > y1) { swap(y0, y1); swap(x0, x1); } if (y0 == y2) { // Handle awkward all-on-same-line case as its own thing a = b = x0; if (x1 < a) a = x1; else if (x1 > b) b = x1; if (x2 < a) a = x2; else if (x2 > b) b = x2; drawFastHLine(a, y0, b - a + 1, color); return; } int16_t dx01 = x1 - x0, dy01 = y1 - y0, dx02 = x2 - x0, dy02 = y2 - y0, dx12 = x2 - x1, dy12 = y2 - y1, sa = 0, sb = 0; // For upper part of triangle, find scanline crossings for segments // 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1 // is included here (and second loop will be skipped, avoiding a /0 // error there), otherwise scanline y1 is skipped here and handled // in the second loop...which also avoids a /0 error here if y0=y1 // (flat-topped triangle). if (y1 == y2) last = y1; // Include y1 scanline else last = y1 - 1; // Skip it #if SPI_MODE_DMA fillScanline(color, max(x0, max(x1, x2)) - min(x0, min(x1, x2))); // fill scanline with the widest scanline that'll be used #endif enableCS(); for (y = y0; y <= last; y++) { a = x0 + sa / dy01; b = x0 + sb / dy02; sa += dx01; sb += dx02; /* longhand: a = x0 + (x1 - x0) * (y - y0) / (y1 - y0); b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); */ if (a > b) swap(a, b); writeHLine_cont_noCS_noFill(a, y, b - a + 1, color); } // For lower part of triangle, find scanline crossings for segments // 0-2 and 1-2. This loop is skipped if y1=y2. sa = dx12 * (y - y1); sb = dx02 * (y - y0); for (; y <= y2; y++) { a = x1 + sa / dy12; b = x0 + sb / dy02; sa += dx12; sb += dx02; /* longhand: a = x1 + (x2 - x1) * (y - y1) / (y2 - y1); b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); */ if (a > b) swap(a, b); writeHLine_cont_noCS_noFill(a, y, b - a + 1, color); } disableCS(); } // draws monochrome (single color) bitmaps void ILI9341_due::drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color) { int16_t i, j, byteWidth = (w + 7) / 8; #if SPI_MODE_DMA _hiByte = highByte(color); _loByte = lowByte(color); fillScanline(color, w); #endif for (j = 0; j < h; j++) { for (i = 0; i < w; i++) { if (pgm_read_byte(bitmap + j * byteWidth + i / 8) & (128 >> (i & 7))) { #if SPI_MODE_NORMAL | SPI_MODE_EXTENDED drawPixel(x+i, y+j, color); #elif SPI_MODE_DMA _scanlineBuffer[i << 1] = _hiByte; _scanlineBuffer[(i << 1) + 1] = _loByte; #endif } } #if SPI_MODE_DMA setAddrAndRW_cont(x + i, y + j, x + w - 1, y + j); writeScanline_cont(w); #endif } disableCS(); } #ifdef FEATURE_PRINT_ENABLED size_t ILI9341_due::write(uint8_t c) { if (c == '\n') { _cursorY += _textsize * 8; _cursorX = 0; } else if (c == '\r') { // skip em } else { drawChar(_cursorX, _cursorY, c, _textcolor, _textbgcolor, _textsize); _cursorX += _textsize * 6; if (_wrap && (_cursorX > (_width - _textsize * 6))) { _cursorY += _textsize * 8; _cursorX = 0; } } return 1; } // Draw a character void ILI9341_due::drawChar(int16_t x, int16_t y, unsigned char c, uint16_t fgcolor, uint16_t bgcolor, uint8_t size) { if ((x >= _width) || // Clip right (y >= _height) || // Clip bottom ((x + 6 * size - 1) < 0) || // Clip left TODO: is this correct? ((y + 8 * size - 1) < 0)) // Clip top TODO: is this correct? return; enableCS(); if (fgcolor == bgcolor) { // This transparent approach is only about 20% faster if (size == 1) { uint8_t mask = 0x01; int16_t xoff, yoff; #if SPI_MODE_DMA fillScanline(fgcolor, 5); #endif for (yoff = 0; yoff < 8; yoff++) { uint8_t line = 0; for (xoff = 0; xoff < 5; xoff++) { if (font[c * 5 + xoff] & mask) line |= 1; line <<= 1; } line >>= 1; xoff = 0; while (line) { if (line == 0x1F) { writeHLine_cont_noCS_noFill(x + xoff, y + yoff, 5, fgcolor); break; } else if (line == 0x1E) { writeHLine_cont_noCS_noFill(x + xoff, y + yoff, 4, fgcolor); break; } else if ((line & 0x1C) == 0x1C) { writeHLine_cont_noCS_noFill(x + xoff, y + yoff, 3, fgcolor); line <<= 4; xoff += 4; } else if ((line & 0x18) == 0x18) { writeHLine_cont_noCS_noFill(x + xoff, y + yoff, 2, fgcolor); line <<= 3; xoff += 3; } else if ((line & 0x10) == 0x10) { writePixel_cont_noCS(x + xoff, y + yoff, fgcolor); line <<= 2; xoff += 2; } else { line <<= 1; xoff += 1; } } mask = mask << 1; } } else { uint8_t mask = 0x01; int16_t xoff, yoff; for (yoff = 0; yoff < 8; yoff++) { uint8_t line = 0; for (xoff = 0; xoff < 5; xoff++) { if (font[c * 5 + xoff] & mask) line |= 1; line <<= 1; } line >>= 1; xoff = 0; while (line) { if (line == 0x1F) { fillRect(x + xoff * size, y + yoff * size, 5 * size, size, fgcolor); break; } else if (line == 0x1E) { fillRect(x + xoff * size, y + yoff * size, 4 * size, size, fgcolor); break; } else if ((line & 0x1C) == 0x1C) { fillRect(x + xoff * size, y + yoff * size, 3 * size, size, fgcolor); line <<= 4; xoff += 4; } else if ((line & 0x18) == 0x18) { fillRect(x + xoff * size, y + yoff * size, 2 * size, size, fgcolor); line <<= 3; xoff += 3; } else if ((line & 0x10) == 0x10) { fillRect(x + xoff * size, y + yoff * size, size, size, fgcolor); line <<= 2; xoff += 2; } else { line <<= 1; xoff += 1; } } mask = mask << 1; } } } else { // This solid background approach is about 5 time faster setAddrAndRW_cont(x, y, x + 6 * size - 1, y + 8 * size); setDCForData(); uint8_t xr, yr; uint8_t mask = 0x01; uint16_t color; uint16_t scanlineId = 0; // for each pixel row for (y = 0; y < 8; y++) { //scanlineId = 0; for (yr = 0; yr < size; yr++) { scanlineId = 0; // draw 5px horizontal "bitmap" line for (x = 0; x < 5; x++) { if (font[c * 5 + x] & mask) { color = fgcolor; } else { color = bgcolor; } for (xr = 0; xr < size; xr++) { #if SPI_MODE_NORMAL | SPI_MODE_EXTENDED write16_cont(color); #elif SPI_MODE_DMA _scanlineBuffer[scanlineId++] = highByte(color); _scanlineBuffer[scanlineId++] = lowByte(color); #endif } } // draw a gap between chars (1px for size of 1) for (xr = 0; xr < size; xr++) { #if SPI_MODE_NORMAL | SPI_MODE_EXTENDED write16_cont(bgcolor); #elif SPI_MODE_DMA _scanlineBuffer[scanlineId++] = highByte(bgcolor); _scanlineBuffer[scanlineId++] = lowByte(bgcolor); #endif } #if SPI_MODE_DMA writeScanline_cont(scanlineId - 1); #endif } mask = mask << 1; } // draw an empty line below a character #if SPI_MODE_NORMAL | SPI_MODE_EXTENDED uint32_t n = 6 * size * size; do { write16_cont(bgcolor); n--; } while (n > 0); #elif SPI_MODE_DMA fillScanline(bgcolor, 6 * size); for (y = 0; y < size; y++) { writeScanline_cont(6 * size); } #endif } disableCS(); } void ILI9341_due::setCursor(int16_t x, int16_t y) { _cursorX = x; _cursorY = y; } void ILI9341_due::setTextSize(uint8_t s) { _textsize = (s > 0) ? s : 1; } void ILI9341_due::setTextColor(uint16_t c) { // For 'transparent' background, we'll set the bg // to the same as fg instead of using a flag _textcolor = _textbgcolor = c; } void ILI9341_due::setTextColor(uint16_t c, uint16_t b) { _textcolor = c; _textbgcolor = b; } void ILI9341_due::setTextWrap(boolean w) { _wrap = w; } #endif uint8_t ILI9341_due::getRotation(void) { return _rotation; } // if true, tft will be blank (white), // display's frame buffer is unaffected // (you can write to it without showing content on the screen) void ILI9341_due::display(boolean d){ writecommand_last(d ? ILI9341_DISPON : ILI9341_DISPOFF); } // puts display in/out of sleep mode void ILI9341_due::sleep(boolean s) { writecommand_last(s ? ILI9341_SLPIN : ILI9341_SLPOUT); delay(120); } void ILI9341_due::idle(boolean i){ writecommand_last(i ? ILI9341_IDMON : ILI9341_IDMOFF); } void ILI9341_due::setPowerLevel(pwrLevel p) { switch (p) { case PWRLEVEL_NORMAL: if (_isIdle) { idle(false); _isIdle = false; } if (_isInSleep) { sleep(false); _isInSleep = false; } break; case PWRLEVEL_IDLE: if (!_isIdle) { idle(true); _isIdle = true; } if (_isInSleep) { sleep(false); _isInSleep = false; } break; case PWRLEVEL_SLEEP: if (!_isInSleep) { sleep(true); _isInSleep = true; } if (_isIdle) { idle(false); _isIdle = false; } break; } } #ifdef FEATURE_ARC_ENABLED void ILI9341_due::setArcParams(float arcAngleMax, int arcAngleOffset) { _arcAngleMax = arcAngleMax; _arcAngleOffset = arcAngleOffset; } #endif //uint8_t ILI9341_due::spiread(void) { // uint8_t r = 0; // // //SPI.setClockDivider(_cs, 12); // 8-ish MHz (full! speed!) // //SPI.setBitOrder(_cs, MSBFIRST); // //SPI.setDataMode(_cs, SPI_MODE0); // r = SPI.transfer(_cs, 0x00); // Serial.print("read: 0x"); Serial.print(r, HEX); // // return r; //} // //void ILI9341_due::spiwrite(uint8_t c) { // // //Serial.print("0x"); Serial.print(c, HEX); Serial.print(", "); // // // //SPI.setClockDivider(_cs, 12); // 8-ish MHz (full! speed!) // //SPI.setBitOrder(_cs, MSBFIRST); // //SPI.setDataMode(_cs, SPI_MODE0); // SPI.transfer(_cs, c); // //} //void ILI9341_due::writecommand(uint8_t c) { // //*dcport &= ~dcpinmask; // digitalWrite(_dc, LOW); // //*clkport &= ~clkpinmask; // clkport is a NULL pointer when hwSPI==true // //digitalWrite(_sclk, LOW); // //*csport &= ~cspinmask; // //digitalWrite(_cs, LOW); // // spiwrite(c); // // //*csport |= cspinmask; // //digitalWrite(_cs, HIGH); //} // // //void ILI9341_due::writedata(uint8_t c) { // //*dcport |= dcpinmask; // digitalWrite(_dc, HIGH); // //*clkport &= ~clkpinmask; // clkport is a NULL pointer when hwSPI==true // //digitalWrite(_sclk, LOW); // //*csport &= ~cspinmask; // //digitalWrite(_cs, LOW); // // spiwrite(c); // // //digitalWrite(_cs, HIGH); // //*csport |= cspinmask; //} void ILI9341_due::printHex8(uint8_t *data, uint8_t length) // prints 8-bit data in hex { char tmp[length * 2 + 1]; byte first; byte second; for (int i = 0; i < length; i++) { first = (data[i] >> 4) & 0x0f; second = data[i] & 0x0f; // base for converting single digit numbers to ASCII is 48 // base for 10-16 to become upper-case characters A-F is 55 // note: difference is 7 tmp[i * 2] = first + 48; tmp[i * 2 + 1] = second + 48; if (first > 9) tmp[i * 2] += 7; if (second > 9) tmp[i * 2 + 1] += 7; } tmp[length * 2] = 0; Serial.print(tmp); } void ILI9341_due::printHex16(uint16_t *data, uint8_t length) // prints 8-bit data in hex { char tmp[length * 4 + 1]; byte first; byte second; byte third; byte fourth; for (int i = 0; i < length; i++) { first = (data[i] >> 12) & 0x0f; second = (data[i] >> 8) & 0x0f; third = (data[i] >> 4) & 0x0f; fourth = data[i] & 0x0f; //Serial << first << " " << second << " " << third << " " << fourth << endl; // base for converting single digit numbers to ASCII is 48 // base for 10-16 to become upper-case characters A-F is 55 // note: difference is 7 tmp[i * 4] = first + 48; tmp[i * 4 + 1] = second + 48; tmp[i * 4 + 2] = third + 48; tmp[i * 4 + 3] = fourth + 48; //tmp[i*5+4] = 32; // add trailing space if (first > 9) tmp[i * 4] += 7; if (second > 9) tmp[i * 4 + 1] += 7; if (third > 9) tmp[i * 4 + 2] += 7; if (fourth > 9) tmp[i * 4 + 3] += 7; } tmp[length * 4] = 0; Serial.print(tmp); } void ILI9341_due::printHex32(uint32_t *data, uint8_t length) // prints 8-bit data in hex { char tmp[length * 8 + 1]; byte dataByte[8]; for (int i = 0; i < length; i++) { dataByte[0] = (data[i] >> 28) & 0x0f; dataByte[1] = (data[i] >> 24) & 0x0f; dataByte[2] = (data[i] >> 20) & 0x0f; dataByte[3] = (data[i] >> 16) & 0x0f; dataByte[4] = (data[i] >> 12) & 0x0f; dataByte[5] = (data[i] >> 8) & 0x0f; dataByte[6] = (data[i] >> 4) & 0x0f; dataByte[7] = data[i] & 0x0f; //Serial << first << " " << second << " " << third << " " << fourth << endl; // base for converting single digit numbers to ASCII is 48 // base for 10-16 to become upper-case characters A-F is 55 // note: difference is 7 tmp[i * 4] = dataByte[0] + 48; tmp[i * 4 + 1] = dataByte[1] + 48; tmp[i * 4 + 2] = dataByte[2] + 48; tmp[i * 4 + 3] = dataByte[3] + 48; tmp[i * 4 + 4] = dataByte[4] + 48; tmp[i * 4 + 5] = dataByte[5] + 48; tmp[i * 4 + 6] = dataByte[6] + 48; tmp[i * 4 + 7] = dataByte[7] + 48; //tmp[i*5+4] = 32; // add trailing space if (dataByte[0] > 9) tmp[i * 4] += 7; if (dataByte[1] > 9) tmp[i * 4 + 1] += 7; if (dataByte[2] > 9) tmp[i * 4 + 2] += 7; if (dataByte[3] > 9) tmp[i * 4 + 3] += 7; if (dataByte[4] > 9) tmp[i * 4 + 4] += 7; if (dataByte[5] > 9) tmp[i * 4 + 5] += 7; if (dataByte[6] > 9) tmp[i * 4 + 6] += 7; if (dataByte[7] > 9) tmp[i * 4 + 7] += 7; } tmp[length * 8] = 0; Serial.print(tmp); }