mirror of
https://github.com/jaksatomovic/esp32-clickwheel.git
synced 2025-08-29 15:48:29 -04:00
first commit
This commit is contained in:
commit
868b14f8c8
286 changed files with 59925 additions and 0 deletions
107
lib/TFT_eSPI/Extensions/Button.cpp
Normal file
107
lib/TFT_eSPI/Extensions/Button.cpp
Normal file
|
@ -0,0 +1,107 @@
|
|||
/***************************************************************************************
|
||||
** Code for the GFX button UI element
|
||||
** Grabbed from Adafruit_GFX library and enhanced to handle any label font
|
||||
***************************************************************************************/
|
||||
TFT_eSPI_Button::TFT_eSPI_Button(void) {
|
||||
_gfx = nullptr;
|
||||
_xd = 0;
|
||||
_yd = 0;
|
||||
_textdatum = MC_DATUM;
|
||||
_label[9] = '\0';
|
||||
currstate = false;
|
||||
laststate = false;
|
||||
}
|
||||
|
||||
// Classic initButton() function: pass center & size
|
||||
void TFT_eSPI_Button::initButton(
|
||||
TFT_eSPI *gfx, int16_t x, int16_t y, uint16_t w, uint16_t h,
|
||||
uint16_t outline, uint16_t fill, uint16_t textcolor,
|
||||
char *label, uint8_t textsize)
|
||||
{
|
||||
// Tweak arguments and pass to the newer initButtonUL() function...
|
||||
initButtonUL(gfx, x - (w / 2), y - (h / 2), w, h, outline, fill,
|
||||
textcolor, label, textsize);
|
||||
}
|
||||
|
||||
// Newer function instead accepts upper-left corner & size
|
||||
void TFT_eSPI_Button::initButtonUL(
|
||||
TFT_eSPI *gfx, int16_t x1, int16_t y1, uint16_t w, uint16_t h,
|
||||
uint16_t outline, uint16_t fill, uint16_t textcolor,
|
||||
char *label, uint8_t textsize)
|
||||
{
|
||||
_x1 = x1;
|
||||
_y1 = y1;
|
||||
_w = w;
|
||||
_h = h;
|
||||
_outlinecolor = outline;
|
||||
_fillcolor = fill;
|
||||
_textcolor = textcolor;
|
||||
_textsize = textsize;
|
||||
_gfx = gfx;
|
||||
strncpy(_label, label, 9);
|
||||
}
|
||||
|
||||
// Adjust text datum and x, y deltas
|
||||
void TFT_eSPI_Button::setLabelDatum(int16_t x_delta, int16_t y_delta, uint8_t datum)
|
||||
{
|
||||
_xd = x_delta;
|
||||
_yd = y_delta;
|
||||
_textdatum = datum;
|
||||
}
|
||||
|
||||
void TFT_eSPI_Button::drawButton(bool inverted, String long_name) {
|
||||
uint16_t fill, outline, text;
|
||||
|
||||
if(!inverted) {
|
||||
fill = _fillcolor;
|
||||
outline = _outlinecolor;
|
||||
text = _textcolor;
|
||||
} else {
|
||||
fill = _textcolor;
|
||||
outline = _outlinecolor;
|
||||
text = _fillcolor;
|
||||
}
|
||||
|
||||
uint8_t r = min(_w, _h) / 4; // Corner radius
|
||||
_gfx->fillRoundRect(_x1, _y1, _w, _h, r, fill);
|
||||
_gfx->drawRoundRect(_x1, _y1, _w, _h, r, outline);
|
||||
|
||||
if (_gfx->textfont == 255) {
|
||||
_gfx->setCursor(_x1 + (_w / 8),
|
||||
_y1 + (_h / 4));
|
||||
_gfx->setTextColor(text);
|
||||
_gfx->setTextSize(_textsize);
|
||||
_gfx->print(_label);
|
||||
}
|
||||
else {
|
||||
_gfx->setTextColor(text, fill);
|
||||
_gfx->setTextSize(_textsize);
|
||||
|
||||
uint8_t tempdatum = _gfx->getTextDatum();
|
||||
_gfx->setTextDatum(_textdatum);
|
||||
uint16_t tempPadding = _gfx->getTextPadding();
|
||||
_gfx->setTextPadding(0);
|
||||
|
||||
if (long_name == "")
|
||||
_gfx->drawString(_label, _x1 + (_w/2) + _xd, _y1 + (_h/2) - 4 + _yd);
|
||||
else
|
||||
_gfx->drawString(long_name, _x1 + (_w/2) + _xd, _y1 + (_h/2) - 4 + _yd);
|
||||
|
||||
_gfx->setTextDatum(tempdatum);
|
||||
_gfx->setTextPadding(tempPadding);
|
||||
}
|
||||
}
|
||||
|
||||
bool TFT_eSPI_Button::contains(int16_t x, int16_t y) {
|
||||
return ((x >= _x1) && (x < (_x1 + _w)) &&
|
||||
(y >= _y1) && (y < (_y1 + _h)));
|
||||
}
|
||||
|
||||
void TFT_eSPI_Button::press(bool p) {
|
||||
laststate = currstate;
|
||||
currstate = p;
|
||||
}
|
||||
|
||||
bool TFT_eSPI_Button::isPressed() { return currstate; }
|
||||
bool TFT_eSPI_Button::justPressed() { return (currstate && !laststate); }
|
||||
bool TFT_eSPI_Button::justReleased() { return (!currstate && laststate); }
|
44
lib/TFT_eSPI/Extensions/Button.h
Normal file
44
lib/TFT_eSPI/Extensions/Button.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/***************************************************************************************
|
||||
// The following button class has been ported over from the Adafruit_GFX library so
|
||||
// should be compatible.
|
||||
// A slightly different implementation in this TFT_eSPI library allows the button
|
||||
// legends to be in any font, allow longer labels and to adjust text positioning
|
||||
// within button
|
||||
***************************************************************************************/
|
||||
|
||||
class TFT_eSPI_Button : public TFT_eSPI {
|
||||
|
||||
public:
|
||||
TFT_eSPI_Button(void);
|
||||
// "Classic" initButton() uses centre & size
|
||||
void initButton(TFT_eSPI *gfx, int16_t x, int16_t y,
|
||||
uint16_t w, uint16_t h, uint16_t outline, uint16_t fill,
|
||||
uint16_t textcolor, char *label, uint8_t textsize);
|
||||
|
||||
// New/alt initButton() uses upper-left corner & size
|
||||
void initButtonUL(TFT_eSPI *gfx, int16_t x1, int16_t y1,
|
||||
uint16_t w, uint16_t h, uint16_t outline, uint16_t fill,
|
||||
uint16_t textcolor, char *label, uint8_t textsize);
|
||||
|
||||
// Adjust text datum and x, y deltas
|
||||
void setLabelDatum(int16_t x_delta, int16_t y_delta, uint8_t datum = MC_DATUM);
|
||||
|
||||
void drawButton(bool inverted = false, String long_name = "");
|
||||
bool contains(int16_t x, int16_t y);
|
||||
|
||||
void press(bool p);
|
||||
bool isPressed();
|
||||
bool justPressed();
|
||||
bool justReleased();
|
||||
|
||||
private:
|
||||
TFT_eSPI *_gfx;
|
||||
int16_t _x1, _y1; // Coordinates of top-left corner of button
|
||||
int16_t _xd, _yd; // Button text datum offsets (wrt centre of button)
|
||||
uint16_t _w, _h; // Width and height of button
|
||||
uint8_t _textsize, _textdatum; // Text size multiplier and text datum for button
|
||||
uint16_t _outlinecolor, _fillcolor, _textcolor;
|
||||
char _label[10]; // Button text is 9 chars maximum unless long_name used
|
||||
|
||||
bool currstate, laststate; // Button states
|
||||
};
|
582
lib/TFT_eSPI/Extensions/Smooth_font.cpp
Normal file
582
lib/TFT_eSPI/Extensions/Smooth_font.cpp
Normal file
|
@ -0,0 +1,582 @@
|
|||
// Coded by Bodmer 10/2/18, see license in root directory.
|
||||
// This is part of the TFT_eSPI class and is associated with anti-aliased font functions
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// New anti-aliased (smoothed) font functions added below
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: loadFont
|
||||
** Description: loads parameters from a font vlw array in memory
|
||||
*************************************************************************************x*/
|
||||
void TFT_eSPI::loadFont(const uint8_t array[])
|
||||
{
|
||||
if (array == nullptr) return;
|
||||
fontPtr = (uint8_t*) array;
|
||||
loadFont("", false);
|
||||
}
|
||||
|
||||
#ifdef FONT_FS_AVAILABLE
|
||||
/***************************************************************************************
|
||||
** Function name: loadFont
|
||||
** Description: loads parameters from a font vlw file
|
||||
*************************************************************************************x*/
|
||||
void TFT_eSPI::loadFont(String fontName, fs::FS &ffs)
|
||||
{
|
||||
fontFS = ffs;
|
||||
loadFont(fontName, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: loadFont
|
||||
** Description: loads parameters from a font vlw file
|
||||
*************************************************************************************x*/
|
||||
void TFT_eSPI::loadFont(String fontName, bool flash)
|
||||
{
|
||||
/*
|
||||
The vlw font format does not appear to be documented anywhere, so some reverse
|
||||
engineering has been applied!
|
||||
|
||||
Header of vlw file comprises 6 uint32_t parameters (24 bytes total):
|
||||
1. The gCount (number of character glyphs)
|
||||
2. A version number (0xB = 11 for the one I am using)
|
||||
3. The font size (in points, not pixels)
|
||||
4. Deprecated mboxY parameter (typically set to 0)
|
||||
5. Ascent in pixels from baseline to top of "d"
|
||||
6. Descent in pixels from baseline to bottom of "p"
|
||||
|
||||
Next are gCount sets of values for each glyph, each set comprises 7 int32t parameters (28 bytes):
|
||||
1. Glyph Unicode stored as a 32 bit value
|
||||
2. Height of bitmap bounding box
|
||||
3. Width of bitmap bounding box
|
||||
4. gxAdvance for cursor (setWidth in Processing)
|
||||
5. dY = distance from cursor baseline to top of glyph bitmap (signed value +ve = up)
|
||||
6. dX = distance from cursor to left side of glyph bitmap (signed value -ve = left)
|
||||
7. padding value, typically 0
|
||||
|
||||
The bitmaps start next at 24 + (28 * gCount) bytes from the start of the file.
|
||||
Each pixel is 1 byte, an 8 bit Alpha value which represents the transparency from
|
||||
0xFF foreground colour, 0x00 background. The library uses a linear interpolation
|
||||
between the foreground and background RGB component colours. e.g.
|
||||
pixelRed = ((fgRed * alpha) + (bgRed * (255 - alpha))/255
|
||||
To gain a performance advantage fixed point arithmetic is used with rounding and
|
||||
division by 256 (shift right 8 bits is faster).
|
||||
|
||||
After the bitmaps is:
|
||||
1 byte for font name string length (excludes null)
|
||||
a zero terminated character string giving the font name
|
||||
1 byte for Postscript name string length
|
||||
a zero/one terminated character string giving the font name
|
||||
last byte is 0 for non-anti-aliased and 1 for anti-aliased (smoothed)
|
||||
|
||||
|
||||
Glyph bitmap example is:
|
||||
// Cursor coordinate positions for this and next character are marked by 'C'
|
||||
// C<------- gxAdvance ------->C gxAdvance is how far to move cursor for next glyph cursor position
|
||||
// | |
|
||||
// | | ascent is top of "d", descent is bottom of "p"
|
||||
// +-- gdX --+ ascent
|
||||
// | +-- gWidth--+ | gdX is offset to left edge of glyph bitmap
|
||||
// | + x@.........@x + | gdX may be negative e.g. italic "y" tail extending to left of
|
||||
// | | @@.........@@ | | cursor position, plot top left corner of bitmap at (cursorX + gdX)
|
||||
// | | @@.........@@ gdY | gWidth and gHeight are glyph bitmap dimensions
|
||||
// | | .@@@.....@@@@ | |
|
||||
// | gHeight ....@@@@@..@@ + + <-- baseline
|
||||
// | | ...........@@ |
|
||||
// | | ...........@@ | gdY is the offset to the top edge of the bitmap
|
||||
// | | .@@.......@@. descent plot top edge of bitmap at (cursorY + ascent - gdY)
|
||||
// | + x..@@@@@@@..x | x marks the corner pixels of the bitmap
|
||||
// | |
|
||||
// +---------------------------+ yAdvance is y delta for the next line, font size or (ascent + descent)
|
||||
// some fonts can overlay in y direction so may need a user adjust value
|
||||
|
||||
*/
|
||||
|
||||
if (fontLoaded) unloadFont();
|
||||
|
||||
#ifdef FONT_FS_AVAILABLE
|
||||
if (fontName == "") fs_font = false;
|
||||
else { fontPtr = nullptr; fs_font = true; }
|
||||
|
||||
if (fs_font) {
|
||||
spiffs = flash; // true if font is in SPIFFS
|
||||
|
||||
if(spiffs) fontFS = SPIFFS;
|
||||
|
||||
// Avoid a crash on the ESP32 if the file does not exist
|
||||
if (fontFS.exists("/" + fontName + ".vlw") == false) {
|
||||
Serial.println("Font file " + fontName + " not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
fontFile = fontFS.open( "/" + fontName + ".vlw", "r");
|
||||
|
||||
if(!fontFile) return;
|
||||
|
||||
fontFile.seek(0, fs::SeekSet);
|
||||
}
|
||||
#else
|
||||
// Avoid unused varaible warning
|
||||
fontName = fontName;
|
||||
flash = flash;
|
||||
#endif
|
||||
|
||||
gFont.gArray = (const uint8_t*)fontPtr;
|
||||
|
||||
gFont.gCount = (uint16_t)readInt32(); // glyph count in file
|
||||
readInt32(); // vlw encoder version - discard
|
||||
gFont.yAdvance = (uint16_t)readInt32(); // Font size in points, not pixels
|
||||
readInt32(); // discard
|
||||
gFont.ascent = (uint16_t)readInt32(); // top of "d"
|
||||
gFont.descent = (uint16_t)readInt32(); // bottom of "p"
|
||||
|
||||
// These next gFont values might be updated when the Metrics are fetched
|
||||
gFont.maxAscent = gFont.ascent; // Determined from metrics
|
||||
gFont.maxDescent = gFont.descent; // Determined from metrics
|
||||
gFont.yAdvance = gFont.ascent + gFont.descent;
|
||||
gFont.spaceWidth = gFont.yAdvance / 4; // Guess at space width
|
||||
|
||||
fontLoaded = true;
|
||||
|
||||
// Fetch the metrics for each glyph
|
||||
loadMetrics();
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: loadMetrics
|
||||
** Description: Get the metrics for each glyph and store in RAM
|
||||
*************************************************************************************x*/
|
||||
//#define SHOW_ASCENT_DESCENT
|
||||
void TFT_eSPI::loadMetrics(void)
|
||||
{
|
||||
uint32_t headerPtr = 24;
|
||||
uint32_t bitmapPtr = headerPtr + gFont.gCount * 28;
|
||||
|
||||
#if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT)
|
||||
if ( psramFound() )
|
||||
{
|
||||
gUnicode = (uint16_t*)ps_malloc( gFont.gCount * 2); // Unicode 16 bit Basic Multilingual Plane (0-FFFF)
|
||||
gHeight = (uint8_t*)ps_malloc( gFont.gCount ); // Height of glyph
|
||||
gWidth = (uint8_t*)ps_malloc( gFont.gCount ); // Width of glyph
|
||||
gxAdvance = (uint8_t*)ps_malloc( gFont.gCount ); // xAdvance - to move x cursor
|
||||
gdY = (int16_t*)ps_malloc( gFont.gCount * 2); // offset from bitmap top edge from lowest point in any character
|
||||
gdX = (int8_t*)ps_malloc( gFont.gCount ); // offset for bitmap left edge relative to cursor X
|
||||
gBitmap = (uint32_t*)ps_malloc( gFont.gCount * 4); // seek pointer to glyph bitmap in the file
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
gUnicode = (uint16_t*)malloc( gFont.gCount * 2); // Unicode 16 bit Basic Multilingual Plane (0-FFFF)
|
||||
gHeight = (uint8_t*)malloc( gFont.gCount ); // Height of glyph
|
||||
gWidth = (uint8_t*)malloc( gFont.gCount ); // Width of glyph
|
||||
gxAdvance = (uint8_t*)malloc( gFont.gCount ); // xAdvance - to move x cursor
|
||||
gdY = (int16_t*)malloc( gFont.gCount * 2); // offset from bitmap top edge from lowest point in any character
|
||||
gdX = (int8_t*)malloc( gFont.gCount ); // offset for bitmap left edge relative to cursor X
|
||||
gBitmap = (uint32_t*)malloc( gFont.gCount * 4); // seek pointer to glyph bitmap in the file
|
||||
}
|
||||
|
||||
#ifdef SHOW_ASCENT_DESCENT
|
||||
Serial.print("ascent = "); Serial.println(gFont.ascent);
|
||||
Serial.print("descent = "); Serial.println(gFont.descent);
|
||||
#endif
|
||||
|
||||
#ifdef FONT_FS_AVAILABLE
|
||||
if (fs_font) fontFile.seek(headerPtr, fs::SeekSet);
|
||||
#endif
|
||||
|
||||
uint16_t gNum = 0;
|
||||
|
||||
while (gNum < gFont.gCount)
|
||||
{
|
||||
gUnicode[gNum] = (uint16_t)readInt32(); // Unicode code point value
|
||||
gHeight[gNum] = (uint8_t)readInt32(); // Height of glyph
|
||||
gWidth[gNum] = (uint8_t)readInt32(); // Width of glyph
|
||||
gxAdvance[gNum] = (uint8_t)readInt32(); // xAdvance - to move x cursor
|
||||
gdY[gNum] = (int16_t)readInt32(); // y delta from baseline
|
||||
gdX[gNum] = (int8_t)readInt32(); // x delta from cursor
|
||||
readInt32(); // ignored
|
||||
|
||||
//Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gHeight = "); Serial.println(gHeight[gNum]);
|
||||
//Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gWidth = "); Serial.println(gWidth[gNum]);
|
||||
//Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gxAdvance = "); Serial.println(gxAdvance[gNum]);
|
||||
//Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gdY = "); Serial.println(gdY[gNum]);
|
||||
|
||||
// Different glyph sets have different ascent values not always based on "d", so we could get
|
||||
// the maximum glyph ascent by checking all characters. BUT this method can generate bad values
|
||||
// for non-existent glyphs, so we will reply on processing for the value and disable this code for now...
|
||||
/*
|
||||
if (gdY[gNum] > gFont.maxAscent)
|
||||
{
|
||||
// Try to avoid UTF coding values and characters that tend to give duff values
|
||||
if (((gUnicode[gNum] > 0x20) && (gUnicode[gNum] < 0x7F)) || (gUnicode[gNum] > 0xA0))
|
||||
{
|
||||
gFont.maxAscent = gdY[gNum];
|
||||
#ifdef SHOW_ASCENT_DESCENT
|
||||
Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", maxAscent = "); Serial.println(gFont.maxAscent);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Different glyph sets have different descent values not always based on "p", so get maximum glyph descent
|
||||
if (((int16_t)gHeight[gNum] - (int16_t)gdY[gNum]) > gFont.maxDescent)
|
||||
{
|
||||
// Avoid UTF coding values and characters that tend to give duff values
|
||||
if (((gUnicode[gNum] > 0x20) && (gUnicode[gNum] < 0xA0) && (gUnicode[gNum] != 0x7F)) || (gUnicode[gNum] > 0xFF))
|
||||
{
|
||||
gFont.maxDescent = gHeight[gNum] - gdY[gNum];
|
||||
#ifdef SHOW_ASCENT_DESCENT
|
||||
Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", maxDescent = "); Serial.println(gHeight[gNum] - gdY[gNum]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
gBitmap[gNum] = bitmapPtr;
|
||||
|
||||
bitmapPtr += gWidth[gNum] * gHeight[gNum];
|
||||
|
||||
gNum++;
|
||||
yield();
|
||||
}
|
||||
|
||||
gFont.yAdvance = gFont.maxAscent + gFont.maxDescent;
|
||||
|
||||
gFont.spaceWidth = (gFont.ascent + gFont.descent) * 2/7; // Guess at space width
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: deleteMetrics
|
||||
** Description: Delete the old glyph metrics and free up the memory
|
||||
*************************************************************************************x*/
|
||||
void TFT_eSPI::unloadFont( void )
|
||||
{
|
||||
if (gUnicode)
|
||||
{
|
||||
free(gUnicode);
|
||||
gUnicode = NULL;
|
||||
}
|
||||
|
||||
if (gHeight)
|
||||
{
|
||||
free(gHeight);
|
||||
gHeight = NULL;
|
||||
}
|
||||
|
||||
if (gWidth)
|
||||
{
|
||||
free(gWidth);
|
||||
gWidth = NULL;
|
||||
}
|
||||
|
||||
if (gxAdvance)
|
||||
{
|
||||
free(gxAdvance);
|
||||
gxAdvance = NULL;
|
||||
}
|
||||
|
||||
if (gdY)
|
||||
{
|
||||
free(gdY);
|
||||
gdY = NULL;
|
||||
}
|
||||
|
||||
if (gdX)
|
||||
{
|
||||
free(gdX);
|
||||
gdX = NULL;
|
||||
}
|
||||
|
||||
if (gBitmap)
|
||||
{
|
||||
free(gBitmap);
|
||||
gBitmap = NULL;
|
||||
}
|
||||
|
||||
gFont.gArray = nullptr;
|
||||
|
||||
#ifdef FONT_FS_AVAILABLE
|
||||
if (fs_font && fontFile) fontFile.close();
|
||||
#endif
|
||||
|
||||
fontLoaded = false;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: readInt32
|
||||
** Description: Get a 32 bit integer from the font file
|
||||
*************************************************************************************x*/
|
||||
uint32_t TFT_eSPI::readInt32(void)
|
||||
{
|
||||
uint32_t val = 0;
|
||||
|
||||
#ifdef FONT_FS_AVAILABLE
|
||||
if (fs_font) {
|
||||
val = fontFile.read() << 24;
|
||||
val |= fontFile.read() << 16;
|
||||
val |= fontFile.read() << 8;
|
||||
val |= fontFile.read();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
val = pgm_read_byte(fontPtr++) << 24;
|
||||
val |= pgm_read_byte(fontPtr++) << 16;
|
||||
val |= pgm_read_byte(fontPtr++) << 8;
|
||||
val |= pgm_read_byte(fontPtr++);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: getUnicodeIndex
|
||||
** Description: Get the font file index of a Unicode character
|
||||
*************************************************************************************x*/
|
||||
bool TFT_eSPI::getUnicodeIndex(uint16_t unicode, uint16_t *index)
|
||||
{
|
||||
for (uint16_t i = 0; i < gFont.gCount; i++)
|
||||
{
|
||||
if (gUnicode[i] == unicode)
|
||||
{
|
||||
*index = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: drawGlyph
|
||||
** Description: Write a character to the TFT cursor position
|
||||
*************************************************************************************x*/
|
||||
// Expects file to be open
|
||||
void TFT_eSPI::drawGlyph(uint16_t code)
|
||||
{
|
||||
uint16_t fg = textcolor;
|
||||
uint16_t bg = textbgcolor;
|
||||
|
||||
// Check if cursor has moved
|
||||
if (last_cursor_x != cursor_x)
|
||||
{
|
||||
bg_cursor_x = cursor_x;
|
||||
last_cursor_x = cursor_x;
|
||||
}
|
||||
|
||||
if (code < 0x21)
|
||||
{
|
||||
if (code == 0x20) {
|
||||
if (_fillbg) fillRect(bg_cursor_x, cursor_y, (cursor_x + gFont.spaceWidth) - bg_cursor_x, gFont.yAdvance, bg);
|
||||
cursor_x += gFont.spaceWidth;
|
||||
bg_cursor_x = cursor_x;
|
||||
last_cursor_x = cursor_x;
|
||||
return;
|
||||
}
|
||||
|
||||
if (code == '\n') {
|
||||
cursor_x = 0;
|
||||
bg_cursor_x = 0;
|
||||
last_cursor_x = 0;
|
||||
cursor_y += gFont.yAdvance;
|
||||
if (textwrapY && (cursor_y >= height())) cursor_y = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t gNum = 0;
|
||||
bool found = getUnicodeIndex(code, &gNum);
|
||||
|
||||
if (found)
|
||||
{
|
||||
|
||||
if (textwrapX && (cursor_x + gWidth[gNum] + gdX[gNum] > width()))
|
||||
{
|
||||
cursor_y += gFont.yAdvance;
|
||||
cursor_x = 0;
|
||||
bg_cursor_x = 0;
|
||||
}
|
||||
if (textwrapY && ((cursor_y + gFont.yAdvance) >= height())) cursor_y = 0;
|
||||
if (cursor_x == 0) cursor_x -= gdX[gNum];
|
||||
|
||||
uint8_t* pbuffer = nullptr;
|
||||
const uint8_t* gPtr = (const uint8_t*) gFont.gArray;
|
||||
|
||||
#ifdef FONT_FS_AVAILABLE
|
||||
if (fs_font)
|
||||
{
|
||||
fontFile.seek(gBitmap[gNum], fs::SeekSet);
|
||||
pbuffer = (uint8_t*)malloc(gWidth[gNum]);
|
||||
}
|
||||
#endif
|
||||
|
||||
int16_t cy = cursor_y + gFont.maxAscent - gdY[gNum];
|
||||
int16_t cx = cursor_x + gdX[gNum];
|
||||
|
||||
// if (cx > width() && bg_cursor_x > width()) return;
|
||||
// if (cursor_y > height()) return;
|
||||
|
||||
int16_t fxs = cx;
|
||||
uint32_t fl = 0;
|
||||
int16_t bxs = cx;
|
||||
uint32_t bl = 0;
|
||||
int16_t bx = 0;
|
||||
uint8_t pixel;
|
||||
|
||||
startWrite(); // Avoid slow ESP32 transaction overhead for every pixel
|
||||
|
||||
int16_t fillwidth = 0;
|
||||
int16_t fillheight = 0;
|
||||
|
||||
// Fill area above glyph
|
||||
if (_fillbg) {
|
||||
fillwidth = (cursor_x + gxAdvance[gNum]) - bg_cursor_x;
|
||||
if (fillwidth > 0) {
|
||||
fillheight = gFont.maxAscent - gdY[gNum];
|
||||
// Could be negative
|
||||
if (fillheight > 0) {
|
||||
fillRect(bg_cursor_x, cursor_y, fillwidth, fillheight, textbgcolor);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Could be negative
|
||||
fillwidth = 0;
|
||||
}
|
||||
|
||||
// Fill any area to left of glyph
|
||||
if (bg_cursor_x < cx) fillRect(bg_cursor_x, cy, cx - bg_cursor_x, gHeight[gNum], textbgcolor);
|
||||
// Set x position in glyph area where background starts
|
||||
if (bg_cursor_x > cx) bx = bg_cursor_x - cx;
|
||||
// Fill any area to right of glyph
|
||||
if (cx + gWidth[gNum] < cursor_x + gxAdvance[gNum]) {
|
||||
fillRect(cx + gWidth[gNum], cy, (cursor_x + gxAdvance[gNum]) - (cx + gWidth[gNum]), gHeight[gNum], textbgcolor);
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t y = 0; y < gHeight[gNum]; y++)
|
||||
{
|
||||
#ifdef FONT_FS_AVAILABLE
|
||||
if (fs_font) {
|
||||
if (spiffs)
|
||||
{
|
||||
fontFile.read(pbuffer, gWidth[gNum]);
|
||||
//Serial.println("SPIFFS");
|
||||
}
|
||||
else
|
||||
{
|
||||
endWrite(); // Release SPI for SD card transaction
|
||||
fontFile.read(pbuffer, gWidth[gNum]);
|
||||
startWrite(); // Re-start SPI for TFT transaction
|
||||
//Serial.println("Not SPIFFS");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for (int32_t x = 0; x < gWidth[gNum]; x++)
|
||||
{
|
||||
#ifdef FONT_FS_AVAILABLE
|
||||
if (fs_font) pixel = pbuffer[x];
|
||||
else
|
||||
#endif
|
||||
pixel = pgm_read_byte(gPtr + gBitmap[gNum] + x + gWidth[gNum] * y);
|
||||
|
||||
if (pixel)
|
||||
{
|
||||
if (bl) { drawFastHLine( bxs, y + cy, bl, bg); bl = 0; }
|
||||
if (pixel != 0xFF)
|
||||
{
|
||||
if (fl) {
|
||||
if (fl==1) drawPixel(fxs, y + cy, fg);
|
||||
else drawFastHLine( fxs, y + cy, fl, fg);
|
||||
fl = 0;
|
||||
}
|
||||
if (getColor) bg = getColor(x + cx, y + cy);
|
||||
drawPixel(x + cx, y + cy, alphaBlend(pixel, fg, bg));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fl==0) fxs = x + cx;
|
||||
fl++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fl) { drawFastHLine( fxs, y + cy, fl, fg); fl = 0; }
|
||||
if (_fillbg) {
|
||||
if (x >= bx) {
|
||||
if (bl==0) bxs = x + cx;
|
||||
bl++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fl) { drawFastHLine( fxs, y + cy, fl, fg); fl = 0; }
|
||||
if (bl) { drawFastHLine( bxs, y + cy, bl, bg); bl = 0; }
|
||||
}
|
||||
|
||||
// Fill area below glyph
|
||||
if (fillwidth > 0) {
|
||||
fillheight = (cursor_y + gFont.yAdvance) - (cy + gHeight[gNum]);
|
||||
if (fillheight > 0) {
|
||||
fillRect(bg_cursor_x, cy + gHeight[gNum], fillwidth, fillheight, textbgcolor);
|
||||
}
|
||||
}
|
||||
|
||||
if (pbuffer) free(pbuffer);
|
||||
cursor_x += gxAdvance[gNum];
|
||||
endWrite();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Point code not in font so draw a rectangle and move on cursor
|
||||
drawRect(cursor_x, cursor_y + gFont.maxAscent - gFont.ascent, gFont.spaceWidth, gFont.ascent, fg);
|
||||
cursor_x += gFont.spaceWidth + 1;
|
||||
}
|
||||
bg_cursor_x = cursor_x;
|
||||
last_cursor_x = cursor_x;
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: showFont
|
||||
** Description: Page through all characters in font, td ms between screens
|
||||
*************************************************************************************x*/
|
||||
void TFT_eSPI::showFont(uint32_t td)
|
||||
{
|
||||
if(!fontLoaded) return;
|
||||
|
||||
int16_t cursorX = width(); // Force start of new page to initialise cursor
|
||||
int16_t cursorY = height();// for the first character
|
||||
uint32_t timeDelay = 0; // No delay before first page
|
||||
|
||||
fillScreen(textbgcolor);
|
||||
|
||||
for (uint16_t i = 0; i < gFont.gCount; i++)
|
||||
{
|
||||
// Check if this will need a new screen
|
||||
if (cursorX + gdX[i] + gWidth[i] >= width()) {
|
||||
cursorX = -gdX[i];
|
||||
|
||||
cursorY += gFont.yAdvance;
|
||||
if (cursorY + gFont.maxAscent + gFont.descent >= height()) {
|
||||
cursorX = -gdX[i];
|
||||
cursorY = 0;
|
||||
delay(timeDelay);
|
||||
timeDelay = td;
|
||||
fillScreen(textbgcolor);
|
||||
}
|
||||
}
|
||||
|
||||
setCursor(cursorX, cursorY);
|
||||
drawGlyph(gUnicode[i]);
|
||||
cursorX += gxAdvance[i];
|
||||
yield();
|
||||
}
|
||||
|
||||
delay(timeDelay);
|
||||
fillScreen(textbgcolor);
|
||||
}
|
61
lib/TFT_eSPI/Extensions/Smooth_font.h
Normal file
61
lib/TFT_eSPI/Extensions/Smooth_font.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
// Coded by Bodmer 10/2/18, see license in root directory.
|
||||
// This is part of the TFT_eSPI class and is associated with anti-aliased font functions
|
||||
|
||||
public:
|
||||
|
||||
// These are for the new anti-aliased fonts
|
||||
void loadFont(const uint8_t array[]);
|
||||
#ifdef FONT_FS_AVAILABLE
|
||||
void loadFont(String fontName, fs::FS &ffs);
|
||||
#endif
|
||||
void loadFont(String fontName, bool flash = true);
|
||||
void unloadFont( void );
|
||||
bool getUnicodeIndex(uint16_t unicode, uint16_t *index);
|
||||
|
||||
virtual void drawGlyph(uint16_t code);
|
||||
|
||||
void showFont(uint32_t td);
|
||||
|
||||
// This is for the whole font
|
||||
typedef struct
|
||||
{
|
||||
const uint8_t* gArray; //array start pointer
|
||||
uint16_t gCount; // Total number of characters
|
||||
uint16_t yAdvance; // Line advance
|
||||
uint16_t spaceWidth; // Width of a space character
|
||||
int16_t ascent; // Height of top of 'd' above baseline, other characters may be taller
|
||||
int16_t descent; // Offset to bottom of 'p', other characters may have a larger descent
|
||||
uint16_t maxAscent; // Maximum ascent found in font
|
||||
uint16_t maxDescent; // Maximum descent found in font
|
||||
} fontMetrics;
|
||||
|
||||
fontMetrics gFont = { nullptr, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
// These are for the metrics for each individual glyph (so we don't need to seek this in file and waste time)
|
||||
uint16_t* gUnicode = NULL; //UTF-16 code, the codes are searched so do not need to be sequential
|
||||
uint8_t* gHeight = NULL; //cheight
|
||||
uint8_t* gWidth = NULL; //cwidth
|
||||
uint8_t* gxAdvance = NULL; //setWidth
|
||||
int16_t* gdY = NULL; //topExtent
|
||||
int8_t* gdX = NULL; //leftExtent
|
||||
uint32_t* gBitmap = NULL; //file pointer to greyscale bitmap
|
||||
|
||||
bool fontLoaded = false; // Flags when a anti-aliased font is loaded
|
||||
|
||||
#ifdef FONT_FS_AVAILABLE
|
||||
fs::File fontFile;
|
||||
fs::FS &fontFS = SPIFFS;
|
||||
bool spiffs = true;
|
||||
bool fs_font = false; // For ESP32/8266 use smooth font file or FLASH (PROGMEM) array
|
||||
|
||||
#else
|
||||
bool fontFile = true;
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
void loadMetrics(void);
|
||||
uint32_t readInt32(void);
|
||||
|
||||
uint8_t* fontPtr = nullptr;
|
||||
|
2700
lib/TFT_eSPI/Extensions/Sprite.cpp
Normal file
2700
lib/TFT_eSPI/Extensions/Sprite.cpp
Normal file
File diff suppressed because it is too large
Load diff
188
lib/TFT_eSPI/Extensions/Sprite.h
Normal file
188
lib/TFT_eSPI/Extensions/Sprite.h
Normal file
|
@ -0,0 +1,188 @@
|
|||
/***************************************************************************************
|
||||
// The following class creates Sprites in RAM, graphics can then be drawn in the Sprite
|
||||
// and rendered quickly onto the TFT screen. The class inherits the graphics functions
|
||||
// from the TFT_eSPI class. Some functions are overridden by this class so that the
|
||||
// graphics are written to the Sprite rather than the TFT.
|
||||
***************************************************************************************/
|
||||
|
||||
class TFT_eSprite : public TFT_eSPI {
|
||||
|
||||
public:
|
||||
|
||||
explicit TFT_eSprite(TFT_eSPI *tft);
|
||||
~TFT_eSprite(void);
|
||||
|
||||
// Create a sprite of width x height pixels, return a pointer to the RAM area
|
||||
// Sketch can cast returned value to (uint16_t*) for 16 bit depth if needed
|
||||
// RAM required is:
|
||||
// - 1 bit per pixel for 1 bit colour depth
|
||||
// - 1 nibble per pixel for 4 bit colour (with palette table)
|
||||
// - 1 byte per pixel for 8 bit colour (332 RGB format)
|
||||
// - 2 bytes per pixel for 16 bit color depth (565 RGB format)
|
||||
void* createSprite(int16_t width, int16_t height, uint8_t frames = 1);
|
||||
|
||||
// Returns a pointer to the sprite or nullptr if not created, user must cast to pointer type
|
||||
void* getPointer(void);
|
||||
|
||||
// Returns true if sprite has been created
|
||||
bool created(void);
|
||||
|
||||
// Delete the sprite to free up the RAM
|
||||
void deleteSprite(void);
|
||||
|
||||
// Select the frame buffer for graphics write (for 2 colour ePaper and DMA toggle buffer)
|
||||
// Returns a pointer to the Sprite frame buffer
|
||||
void* frameBuffer(int8_t f);
|
||||
|
||||
// Set or get the colour depth to 1, 4, 8 or 16 bits. Can be used to change depth an existing
|
||||
// sprite, but clears it to black, returns a new pointer if sprite is re-created.
|
||||
void* setColorDepth(int8_t b);
|
||||
int8_t getColorDepth(void);
|
||||
|
||||
// Set the palette for a 4 bit depth sprite. Only the first 16 colours in the map are used.
|
||||
void createPalette(uint16_t *palette = nullptr, uint8_t colors = 16); // Palette in RAM
|
||||
void createPalette(const uint16_t *palette = nullptr, uint8_t colors = 16); // Palette in FLASH
|
||||
|
||||
// Set a single palette index to the given color
|
||||
void setPaletteColor(uint8_t index, uint16_t color);
|
||||
|
||||
// Get the color at the given palette index
|
||||
uint16_t getPaletteColor(uint8_t index);
|
||||
|
||||
// Set foreground and background colours for 1 bit per pixel Sprite
|
||||
void setBitmapColor(uint16_t fg, uint16_t bg);
|
||||
|
||||
// Draw a single pixel at x,y
|
||||
void drawPixel(int32_t x, int32_t y, uint32_t color);
|
||||
|
||||
// Draw a single character in the GLCD or GFXFF font
|
||||
void drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32_t bg, uint8_t size),
|
||||
|
||||
// Fill Sprite with a colour
|
||||
fillSprite(uint32_t color),
|
||||
|
||||
// Define a window to push 16 bit colour pixels into in a raster order
|
||||
// Colours are converted to the set Sprite colour bit depth
|
||||
setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1),
|
||||
// Push a color (aka singe pixel) to the sprite's set window area
|
||||
pushColor(uint16_t color),
|
||||
// Push len colors (pixels) to the sprite's set window area
|
||||
pushColor(uint16_t color, uint32_t len),
|
||||
// Push a pixel pre-formatted as a 1, 4, 8 or 16 bit colour (avoids conversion overhead)
|
||||
writeColor(uint16_t color),
|
||||
|
||||
// Set the scroll zone, top left corner at x,y with defined width and height
|
||||
// The colour (optional, black is default) is used to fill the gap after the scroll
|
||||
setScrollRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t color = TFT_BLACK),
|
||||
// Scroll the defined zone dx,dy pixels. Negative values left,up, positive right,down
|
||||
// dy is optional (default is 0, so no up/down scroll).
|
||||
// The sprite coordinate frame does not move because pixels are moved
|
||||
scroll(int16_t dx, int16_t dy = 0),
|
||||
|
||||
// Draw lines
|
||||
drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color),
|
||||
drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color),
|
||||
drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color),
|
||||
|
||||
// Fill a rectangular area with a color (aka draw a filled rectangle)
|
||||
fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color);
|
||||
|
||||
// Set the coordinate rotation of the Sprite (for 1bpp Sprites only)
|
||||
// Note: this uses coordinate rotation and is primarily for ePaper which does not support
|
||||
// CGRAM rotation (like TFT drivers do) within the displays internal hardware
|
||||
void setRotation(uint8_t rotation);
|
||||
uint8_t getRotation(void);
|
||||
|
||||
// Push a rotated copy of Sprite to TFT with optional transparent colour
|
||||
bool pushRotated(int16_t angle, uint32_t transp = 0x00FFFFFF);
|
||||
// Push a rotated copy of Sprite to another different Sprite with optional transparent colour
|
||||
bool pushRotated(TFT_eSprite *spr, int16_t angle, uint32_t transp = 0x00FFFFFF);
|
||||
|
||||
// Get the TFT bounding box for a rotated copy of this Sprite
|
||||
bool getRotatedBounds(int16_t angle, int16_t *min_x, int16_t *min_y, int16_t *max_x, int16_t *max_y);
|
||||
// Get the destination Sprite bounding box for a rotated copy of this Sprite
|
||||
bool getRotatedBounds(TFT_eSprite *spr, int16_t angle, int16_t *min_x, int16_t *min_y,
|
||||
int16_t *max_x, int16_t *max_y);
|
||||
// Bounding box support function
|
||||
void getRotatedBounds(int16_t angle, int16_t w, int16_t h, int16_t xp, int16_t yp,
|
||||
int16_t *min_x, int16_t *min_y, int16_t *max_x, int16_t *max_y);
|
||||
|
||||
// Read the colour of a pixel at x,y and return value in 565 format
|
||||
uint16_t readPixel(int32_t x0, int32_t y0);
|
||||
|
||||
// return the numerical value of the pixel at x,y (used when scrolling)
|
||||
// 16bpp = colour, 8bpp = byte, 4bpp = colour index, 1bpp = 1 or 0
|
||||
uint16_t readPixelValue(int32_t x, int32_t y);
|
||||
|
||||
// Write an image (colour bitmap) to the sprite.
|
||||
void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, uint16_t *data, uint8_t sbpp = 0);
|
||||
void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, const uint16_t *data);
|
||||
|
||||
// Push the sprite to the TFT screen, this fn calls pushImage() in the TFT class.
|
||||
// Optionally a "transparent" colour can be defined, pixels of that colour will not be rendered
|
||||
void pushSprite(int32_t x, int32_t y);
|
||||
void pushSprite(int32_t x, int32_t y, uint16_t transparent);
|
||||
|
||||
// Push a windowed area of the sprite to the TFT at tx, ty
|
||||
bool pushSprite(int32_t tx, int32_t ty, int32_t sx, int32_t sy, int32_t sw, int32_t sh);
|
||||
|
||||
// Push the sprite to another sprite at x,y. This fn calls pushImage() in the destination sprite (dspr) class.
|
||||
bool pushToSprite(TFT_eSprite *dspr, int32_t x, int32_t y);
|
||||
bool pushToSprite(TFT_eSprite *dspr, int32_t x, int32_t y, uint16_t transparent);
|
||||
|
||||
// Draw a single character in the selected font
|
||||
int16_t drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font),
|
||||
drawChar(uint16_t uniCode, int32_t x, int32_t y);
|
||||
|
||||
// Return the width and height of the sprite
|
||||
int16_t width(void),
|
||||
height(void);
|
||||
|
||||
// Functions associated with anti-aliased fonts
|
||||
// Draw a single unicode character using the loaded font
|
||||
void drawGlyph(uint16_t code);
|
||||
// Print string to sprite using loaded font at cursor position
|
||||
void printToSprite(String string);
|
||||
// Print char array to sprite using loaded font at cursor position
|
||||
void printToSprite(char *cbuffer, uint16_t len);
|
||||
// Print indexed glyph to sprite using loaded font at x,y
|
||||
int16_t printToSprite(int16_t x, int16_t y, uint16_t index);
|
||||
|
||||
private:
|
||||
|
||||
TFT_eSPI *_tft;
|
||||
|
||||
// Reserve memory for the Sprite and return a pointer
|
||||
void* callocSprite(int16_t width, int16_t height, uint8_t frames = 1);
|
||||
|
||||
// Override the non-inlined TFT_eSPI functions
|
||||
void begin_nin_write(void) { ; }
|
||||
void end_nin_write(void) { ; }
|
||||
|
||||
protected:
|
||||
|
||||
uint8_t _bpp; // bits per pixel (1, 4, 8 or 16)
|
||||
uint16_t *_img; // pointer to 16 bit sprite
|
||||
uint8_t *_img8; // pointer to 1 and 8 bit sprite frame 1 or frame 2
|
||||
uint8_t *_img4; // pointer to 4 bit sprite (uses color map)
|
||||
uint8_t *_img8_1; // pointer to frame 1
|
||||
uint8_t *_img8_2; // pointer to frame 2
|
||||
|
||||
uint16_t *_colorMap; // color map pointer: 16 entries, used with 4 bit color map.
|
||||
|
||||
int32_t _sinra; // Sine of rotation angle in fixed point
|
||||
int32_t _cosra; // Cosine of rotation angle in fixed point
|
||||
|
||||
bool _created; // A Sprite has been created and memory reserved
|
||||
bool _gFont = false;
|
||||
|
||||
int32_t _xs, _ys, _xe, _ye, _xptr, _yptr; // for setWindow
|
||||
int32_t _sx, _sy; // x,y for scroll zone
|
||||
uint32_t _sw, _sh; // w,h for scroll zone
|
||||
uint32_t _scolor; // gap fill colour for scroll zone
|
||||
|
||||
int32_t _iwidth, _iheight; // Sprite memory image bit width and height (swapped during rotations)
|
||||
int32_t _dwidth, _dheight; // Real sprite width and height (for <8bpp Sprites)
|
||||
int32_t _bitwidth; // Sprite image bit width for drawPixel (for <8bpp Sprites, not swapped)
|
||||
|
||||
};
|
345
lib/TFT_eSPI/Extensions/Touch.cpp
Normal file
345
lib/TFT_eSPI/Extensions/Touch.cpp
Normal file
|
@ -0,0 +1,345 @@
|
|||
// The following touch screen support code by maxpautsch was merged 1/10/17
|
||||
// https://github.com/maxpautsch
|
||||
|
||||
// Define TOUCH_CS is the user setup file to enable this code
|
||||
|
||||
// A demo is provided in examples Generic folder
|
||||
|
||||
// Additions by Bodmer to double sample, use Z value to improve detection reliability
|
||||
// and to correct rotation handling
|
||||
|
||||
// See license in root directory.
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: begin_touch_read_write - was spi_begin_touch
|
||||
** Description: Start transaction and select touch controller
|
||||
***************************************************************************************/
|
||||
// The touch controller has a low SPI clock rate
|
||||
inline void TFT_eSPI::begin_touch_read_write(void){
|
||||
DMA_BUSY_CHECK;
|
||||
CS_H; // Just in case it has been left low
|
||||
#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS)
|
||||
if (locked) {locked = false; spi.beginTransaction(SPISettings(SPI_TOUCH_FREQUENCY, MSBFIRST, SPI_MODE0));}
|
||||
#else
|
||||
spi.setFrequency(SPI_TOUCH_FREQUENCY);
|
||||
#endif
|
||||
SET_BUS_READ_MODE;
|
||||
T_CS_L;
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: end_touch_read_write - was spi_end_touch
|
||||
** Description: End transaction and deselect touch controller
|
||||
***************************************************************************************/
|
||||
inline void TFT_eSPI::end_touch_read_write(void){
|
||||
T_CS_H;
|
||||
#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS)
|
||||
if(!inTransaction) {if (!locked) {locked = true; spi.endTransaction();}}
|
||||
#else
|
||||
spi.setFrequency(SPI_FREQUENCY);
|
||||
#endif
|
||||
//SET_BUS_WRITE_MODE;
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: Legacy - deprecated
|
||||
** Description: Start/end transaction
|
||||
***************************************************************************************/
|
||||
void TFT_eSPI::spi_begin_touch() {begin_touch_read_write();}
|
||||
void TFT_eSPI::spi_end_touch() { end_touch_read_write();}
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: getTouchRaw
|
||||
** Description: read raw touch position. Always returns true.
|
||||
***************************************************************************************/
|
||||
uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){
|
||||
uint16_t tmp;
|
||||
|
||||
begin_touch_read_write();
|
||||
|
||||
// Start YP sample request for x position, read 4 times and keep last sample
|
||||
spi.transfer(0xd0); // Start new YP conversion
|
||||
spi.transfer(0); // Read first 8 bits
|
||||
spi.transfer(0xd0); // Read last 8 bits and start new YP conversion
|
||||
spi.transfer(0); // Read first 8 bits
|
||||
spi.transfer(0xd0); // Read last 8 bits and start new YP conversion
|
||||
spi.transfer(0); // Read first 8 bits
|
||||
spi.transfer(0xd0); // Read last 8 bits and start new YP conversion
|
||||
|
||||
tmp = spi.transfer(0); // Read first 8 bits
|
||||
tmp = tmp <<5;
|
||||
tmp |= 0x1f & (spi.transfer(0x90)>>3); // Read last 8 bits and start new XP conversion
|
||||
|
||||
*x = tmp;
|
||||
|
||||
// Start XP sample request for y position, read 4 times and keep last sample
|
||||
spi.transfer(0); // Read first 8 bits
|
||||
spi.transfer(0x90); // Read last 8 bits and start new XP conversion
|
||||
spi.transfer(0); // Read first 8 bits
|
||||
spi.transfer(0x90); // Read last 8 bits and start new XP conversion
|
||||
spi.transfer(0); // Read first 8 bits
|
||||
spi.transfer(0x90); // Read last 8 bits and start new XP conversion
|
||||
|
||||
tmp = spi.transfer(0); // Read first 8 bits
|
||||
tmp = tmp <<5;
|
||||
tmp |= 0x1f & (spi.transfer(0)>>3); // Read last 8 bits
|
||||
|
||||
*y = tmp;
|
||||
|
||||
end_touch_read_write();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: getTouchRawZ
|
||||
** Description: read raw pressure on touchpad and return Z value.
|
||||
***************************************************************************************/
|
||||
uint16_t TFT_eSPI::getTouchRawZ(void){
|
||||
|
||||
begin_touch_read_write();
|
||||
|
||||
// Z sample request
|
||||
int16_t tz = 0xFFF;
|
||||
spi.transfer(0xb0); // Start new Z1 conversion
|
||||
tz += spi.transfer16(0xc0) >> 3; // Read Z1 and start Z2 conversion
|
||||
tz -= spi.transfer16(0x00) >> 3; // Read Z2
|
||||
|
||||
end_touch_read_write();
|
||||
|
||||
if (tz == 4095) tz = 0;
|
||||
|
||||
return (uint16_t)tz;
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: validTouch
|
||||
** Description: read validated position. Return false if not pressed.
|
||||
***************************************************************************************/
|
||||
#define _RAWERR 20 // Deadband error allowed in successive position samples
|
||||
uint8_t TFT_eSPI::validTouch(uint16_t *x, uint16_t *y, uint16_t threshold){
|
||||
uint16_t x_tmp, y_tmp, x_tmp2, y_tmp2;
|
||||
|
||||
// Wait until pressure stops increasing to debounce pressure
|
||||
uint16_t z1 = 1;
|
||||
uint16_t z2 = 0;
|
||||
while (z1 > z2)
|
||||
{
|
||||
z2 = z1;
|
||||
z1 = getTouchRawZ();
|
||||
delay(1);
|
||||
}
|
||||
|
||||
// Serial.print("Z = ");Serial.println(z1);
|
||||
|
||||
if (z1 <= threshold) return false;
|
||||
|
||||
getTouchRaw(&x_tmp,&y_tmp);
|
||||
|
||||
// Serial.print("Sample 1 x,y = "); Serial.print(x_tmp);Serial.print(",");Serial.print(y_tmp);
|
||||
// Serial.print(", Z = ");Serial.println(z1);
|
||||
|
||||
delay(1); // Small delay to the next sample
|
||||
if (getTouchRawZ() <= threshold) return false;
|
||||
|
||||
delay(2); // Small delay to the next sample
|
||||
getTouchRaw(&x_tmp2,&y_tmp2);
|
||||
|
||||
// Serial.print("Sample 2 x,y = "); Serial.print(x_tmp2);Serial.print(",");Serial.println(y_tmp2);
|
||||
// Serial.print("Sample difference = ");Serial.print(abs(x_tmp - x_tmp2));Serial.print(",");Serial.println(abs(y_tmp - y_tmp2));
|
||||
|
||||
if (abs(x_tmp - x_tmp2) > _RAWERR) return false;
|
||||
if (abs(y_tmp - y_tmp2) > _RAWERR) return false;
|
||||
|
||||
*x = x_tmp;
|
||||
*y = y_tmp;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: getTouch
|
||||
** Description: read callibrated position. Return false if not pressed.
|
||||
***************************************************************************************/
|
||||
#define Z_THRESHOLD 350 // Touch pressure threshold for validating touches
|
||||
uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y, uint16_t threshold){
|
||||
uint16_t x_tmp, y_tmp;
|
||||
|
||||
if (threshold<20) threshold = 20;
|
||||
if (_pressTime > millis()) threshold=20;
|
||||
|
||||
uint8_t n = 5;
|
||||
uint8_t valid = 0;
|
||||
while (n--)
|
||||
{
|
||||
if (validTouch(&x_tmp, &y_tmp, threshold)) valid++;;
|
||||
}
|
||||
|
||||
if (valid<1) { _pressTime = 0; return false; }
|
||||
|
||||
_pressTime = millis() + 50;
|
||||
|
||||
convertRawXY(&x_tmp, &y_tmp);
|
||||
|
||||
if (x_tmp >= _width || y_tmp >= _height) return false;
|
||||
|
||||
_pressX = x_tmp;
|
||||
_pressY = y_tmp;
|
||||
*x = _pressX;
|
||||
*y = _pressY;
|
||||
return valid;
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: convertRawXY
|
||||
** Description: convert raw touch x,y values to screen coordinates
|
||||
***************************************************************************************/
|
||||
void TFT_eSPI::convertRawXY(uint16_t *x, uint16_t *y)
|
||||
{
|
||||
uint16_t x_tmp = *x, y_tmp = *y, xx, yy;
|
||||
|
||||
if(!touchCalibration_rotate){
|
||||
xx=(x_tmp-touchCalibration_x0)*_width/touchCalibration_x1;
|
||||
yy=(y_tmp-touchCalibration_y0)*_height/touchCalibration_y1;
|
||||
if(touchCalibration_invert_x)
|
||||
xx = _width - xx;
|
||||
if(touchCalibration_invert_y)
|
||||
yy = _height - yy;
|
||||
} else {
|
||||
xx=(y_tmp-touchCalibration_x0)*_width/touchCalibration_x1;
|
||||
yy=(x_tmp-touchCalibration_y0)*_height/touchCalibration_y1;
|
||||
if(touchCalibration_invert_x)
|
||||
xx = _width - xx;
|
||||
if(touchCalibration_invert_y)
|
||||
yy = _height - yy;
|
||||
}
|
||||
*x = xx;
|
||||
*y = yy;
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: calibrateTouch
|
||||
** Description: generates calibration parameters for touchscreen.
|
||||
***************************************************************************************/
|
||||
void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_fg, uint32_t color_bg, uint8_t size){
|
||||
int16_t values[] = {0,0,0,0,0,0,0,0};
|
||||
uint16_t x_tmp, y_tmp;
|
||||
|
||||
|
||||
|
||||
for(uint8_t i = 0; i<4; i++){
|
||||
fillRect(0, 0, size+1, size+1, color_bg);
|
||||
fillRect(0, _height-size-1, size+1, size+1, color_bg);
|
||||
fillRect(_width-size-1, 0, size+1, size+1, color_bg);
|
||||
fillRect(_width-size-1, _height-size-1, size+1, size+1, color_bg);
|
||||
|
||||
if (i == 5) break; // used to clear the arrows
|
||||
|
||||
switch (i) {
|
||||
case 0: // up left
|
||||
drawLine(0, 0, 0, size, color_fg);
|
||||
drawLine(0, 0, size, 0, color_fg);
|
||||
drawLine(0, 0, size , size, color_fg);
|
||||
break;
|
||||
case 1: // bot left
|
||||
drawLine(0, _height-size-1, 0, _height-1, color_fg);
|
||||
drawLine(0, _height-1, size, _height-1, color_fg);
|
||||
drawLine(size, _height-size-1, 0, _height-1 , color_fg);
|
||||
break;
|
||||
case 2: // up right
|
||||
drawLine(_width-size-1, 0, _width-1, 0, color_fg);
|
||||
drawLine(_width-size-1, size, _width-1, 0, color_fg);
|
||||
drawLine(_width-1, size, _width-1, 0, color_fg);
|
||||
break;
|
||||
case 3: // bot right
|
||||
drawLine(_width-size-1, _height-size-1, _width-1, _height-1, color_fg);
|
||||
drawLine(_width-1, _height-1-size, _width-1, _height-1, color_fg);
|
||||
drawLine(_width-1-size, _height-1, _width-1, _height-1, color_fg);
|
||||
break;
|
||||
}
|
||||
|
||||
// user has to get the chance to release
|
||||
if(i>0) delay(1000);
|
||||
|
||||
for(uint8_t j= 0; j<8; j++){
|
||||
// Use a lower detect threshold as corners tend to be less sensitive
|
||||
while(!validTouch(&x_tmp, &y_tmp, Z_THRESHOLD/2));
|
||||
values[i*2 ] += x_tmp;
|
||||
values[i*2+1] += y_tmp;
|
||||
}
|
||||
values[i*2 ] /= 8;
|
||||
values[i*2+1] /= 8;
|
||||
}
|
||||
|
||||
|
||||
// from case 0 to case 1, the y value changed.
|
||||
// If the measured delta of the touch x axis is bigger than the delta of the y axis, the touch and TFT axes are switched.
|
||||
touchCalibration_rotate = false;
|
||||
if(abs(values[0]-values[2]) > abs(values[1]-values[3])){
|
||||
touchCalibration_rotate = true;
|
||||
touchCalibration_x0 = (values[1] + values[3])/2; // calc min x
|
||||
touchCalibration_x1 = (values[5] + values[7])/2; // calc max x
|
||||
touchCalibration_y0 = (values[0] + values[4])/2; // calc min y
|
||||
touchCalibration_y1 = (values[2] + values[6])/2; // calc max y
|
||||
} else {
|
||||
touchCalibration_x0 = (values[0] + values[2])/2; // calc min x
|
||||
touchCalibration_x1 = (values[4] + values[6])/2; // calc max x
|
||||
touchCalibration_y0 = (values[1] + values[5])/2; // calc min y
|
||||
touchCalibration_y1 = (values[3] + values[7])/2; // calc max y
|
||||
}
|
||||
|
||||
// in addition, the touch screen axis could be in the opposite direction of the TFT axis
|
||||
touchCalibration_invert_x = false;
|
||||
if(touchCalibration_x0 > touchCalibration_x1){
|
||||
values[0]=touchCalibration_x0;
|
||||
touchCalibration_x0 = touchCalibration_x1;
|
||||
touchCalibration_x1 = values[0];
|
||||
touchCalibration_invert_x = true;
|
||||
}
|
||||
touchCalibration_invert_y = false;
|
||||
if(touchCalibration_y0 > touchCalibration_y1){
|
||||
values[0]=touchCalibration_y0;
|
||||
touchCalibration_y0 = touchCalibration_y1;
|
||||
touchCalibration_y1 = values[0];
|
||||
touchCalibration_invert_y = true;
|
||||
}
|
||||
|
||||
// pre calculate
|
||||
touchCalibration_x1 -= touchCalibration_x0;
|
||||
touchCalibration_y1 -= touchCalibration_y0;
|
||||
|
||||
if(touchCalibration_x0 == 0) touchCalibration_x0 = 1;
|
||||
if(touchCalibration_x1 == 0) touchCalibration_x1 = 1;
|
||||
if(touchCalibration_y0 == 0) touchCalibration_y0 = 1;
|
||||
if(touchCalibration_y1 == 0) touchCalibration_y1 = 1;
|
||||
|
||||
// export parameters, if pointer valid
|
||||
if(parameters != NULL){
|
||||
parameters[0] = touchCalibration_x0;
|
||||
parameters[1] = touchCalibration_x1;
|
||||
parameters[2] = touchCalibration_y0;
|
||||
parameters[3] = touchCalibration_y1;
|
||||
parameters[4] = touchCalibration_rotate | (touchCalibration_invert_x <<1) | (touchCalibration_invert_y <<2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: setTouch
|
||||
** Description: imports calibration parameters for touchscreen.
|
||||
***************************************************************************************/
|
||||
void TFT_eSPI::setTouch(uint16_t *parameters){
|
||||
touchCalibration_x0 = parameters[0];
|
||||
touchCalibration_x1 = parameters[1];
|
||||
touchCalibration_y0 = parameters[2];
|
||||
touchCalibration_y1 = parameters[3];
|
||||
|
||||
if(touchCalibration_x0 == 0) touchCalibration_x0 = 1;
|
||||
if(touchCalibration_x1 == 0) touchCalibration_x1 = 1;
|
||||
if(touchCalibration_y0 == 0) touchCalibration_y0 = 1;
|
||||
if(touchCalibration_y1 == 0) touchCalibration_y1 = 1;
|
||||
|
||||
touchCalibration_rotate = parameters[4] & 0x01;
|
||||
touchCalibration_invert_x = parameters[4] & 0x02;
|
||||
touchCalibration_invert_y = parameters[4] & 0x04;
|
||||
}
|
39
lib/TFT_eSPI/Extensions/Touch.h
Normal file
39
lib/TFT_eSPI/Extensions/Touch.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
// Coded by Bodmer 10/2/18, see license in root directory.
|
||||
// This is part of the TFT_eSPI class and is associated with the Touch Screen handlers
|
||||
|
||||
public:
|
||||
// Get raw x,y ADC values from touch controller
|
||||
uint8_t getTouchRaw(uint16_t *x, uint16_t *y);
|
||||
// Get raw z (i.e. pressure) ADC value from touch controller
|
||||
uint16_t getTouchRawZ(void);
|
||||
// Convert raw x,y values to calibrated and correctly rotated screen coordinates
|
||||
void convertRawXY(uint16_t *x, uint16_t *y);
|
||||
// Get the screen touch coordinates, returns true if screen has been touched
|
||||
// if the touch coordinates are off screen then x and y are not updated
|
||||
// The returned value can be treated as a bool type, false or 0 means touch not detected
|
||||
// In future the function may return an 8 "quality" (jitter) value.
|
||||
uint8_t getTouch(uint16_t *x, uint16_t *y, uint16_t threshold = 600);
|
||||
|
||||
// Run screen calibration and test, report calibration values to the serial port
|
||||
void calibrateTouch(uint16_t *data, uint32_t color_fg, uint32_t color_bg, uint8_t size);
|
||||
// Set the screen calibration values
|
||||
void setTouch(uint16_t *data);
|
||||
|
||||
private:
|
||||
// Legacy support only - deprecated TODO: delete
|
||||
void spi_begin_touch();
|
||||
void spi_end_touch();
|
||||
|
||||
// Handlers for the touch controller bus settings
|
||||
inline void begin_touch_read_write() __attribute__((always_inline));
|
||||
inline void end_touch_read_write() __attribute__((always_inline));
|
||||
|
||||
// Private function to validate a touch, allow settle time and reduce spurious coordinates
|
||||
uint8_t validTouch(uint16_t *x, uint16_t *y, uint16_t threshold = 600);
|
||||
|
||||
// Initialise with example calibration values so processor does not crash if setTouch() not called in setup()
|
||||
uint16_t touchCalibration_x0 = 300, touchCalibration_x1 = 3600, touchCalibration_y0 = 300, touchCalibration_y1 = 3600;
|
||||
uint8_t touchCalibration_rotate = 1, touchCalibration_invert_x = 2, touchCalibration_invert_y = 0;
|
||||
|
||||
uint32_t _pressTime; // Press and hold time-out
|
||||
uint16_t _pressX, _pressY; // For future use (last sampled calibrated coordinates)
|
Loading…
Add table
Add a link
Reference in a new issue