/*
Part of RA8875 library from https://github.com/sumotoy/RA8875
License:GNU General Public License v3.0
RA8875 fast SPI library for RAiO SPI RA8875 drived TFT
Copyright (C) 2014 egidio massimo costa sumotoy (a t) gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(SPARK)//SPI already included in applications.h
#include
#endif
#include "RA8875.h"
#if defined (USE_FT5206_TOUCH)
#if !defined(SPARK)//wire it's already included in applications.h
#include
#if defined(___DUESTUFF) && defined(USE_DUE_WIRE1_INTERFACE)
#define Wire Wire1
#endif
#endif
const uint8_t _ctpAdrs = 0x38;
const uint8_t coordRegStart[5] = {0x03,0x09,0x0F,0x15,0x1B};
static volatile bool _FT5206_INT = false;
#endif
//static volatile uint8_t _RA8875_INTS = 0b00000000;//container for INT states
RA8875 *RA8875::_active_touch_objects[3] = {nullptr, nullptr, nullptr};
/*------------------------------
Bit: Called by: In use:
--------------------------------
0: isr triggered [*]
1: Resistive TS [*]
2: KeyScan [*]
3: DMA
4: BTE
5: FT5206 TS [*]
6: -na-
7: -na-
--------------------------------*/
/**************************************************************************/
/*!
Contructors
CSp: SPI SS pin
RSTp: Reset pin
INTp: INT pin
//Teensy 3.0 , 3.1 , LC
mosi_pin
sclk_pin
miso_pin
Note:
Teensy CS SPI1:[MOSI1(0) MISO1(1) CLK1(20) CS1(6)]
*/
/**************************************************************************/
//------------------------------TEENSY 3/3.1 ---------------------------------------
#if defined(__MK20DX128__) || defined(__MK20DX256__)
RA8875::RA8875(const uint8_t CSp,const uint8_t RSTp,const uint8_t mosi_pin,const uint8_t sclk_pin,const uint8_t miso_pin)
{
_mosi = mosi_pin;
_miso = miso_pin;
_sclk = sclk_pin;
_cs = CSp;
_rst = RSTp;
_RA8875_INTS = 0b00000000;
//------------------------------Teensy LC-------------------------------------------
#elif defined(__MKL26Z64__)
RA8875::RA8875(const uint8_t CSp,const uint8_t RSTp,const uint8_t mosi_pin,const uint8_t sclk_pin,const uint8_t miso_pin)
{
_mosi = mosi_pin;
_miso = miso_pin;
_sclk = sclk_pin;
_cs = CSp;
_rst = RSTp;
_pspi = nullptr;
_RA8875_INTS = 0b00000000;
//------------------------------Teensy of the future -------------------------------------------
#elif defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__)
RA8875::RA8875(const uint8_t CSp,const uint8_t RSTp,const uint8_t mosi_pin,const uint8_t sclk_pin,const uint8_t miso_pin)
{
_mosi = mosi_pin;
_miso = miso_pin;
_sclk = sclk_pin;
_cs = CSp;
_rst = RSTp;
_pspi = nullptr;
_RA8875_INTS = 0b00000000;
//---------------------------------DUE--------------------------------------------
#elif defined(___DUESTUFF)//DUE
RA8875::RA8875(const uint8_t CSp, const uint8_t RSTp)
{
_cs = CSp;
_rst = RSTp;
_RA8875_INTS = 0b00000000;
//---------------------------------SPARK----------------------------------------
#elif defined(SPARK)//SPARK
RA8875::RA8875(const uint8_t CSp, const uint8_t RSTp)
{
_cs = CSp;
_rst = RSTp;
_RA8875_INTS = 0b00000000;
//------------------------------ENERGIA-------------------------------------------
#elif defined(NEEDS_SET_MODULE)
RA8875::RA8875(const uint8_t module, const uint8_t RSTp)
{
selectCS(module);
_rst = RSTp;
_cs = 255;
_RA8875_INTS = 0b00000000;
//----------------------------8 BIT ARDUINO's---------------------------------------
#else
RA8875::RA8875(const uint8_t CSp, const uint8_t RSTp)
{
_cs = CSp;
_rst = RSTp;
_RA8875_INTS = 0b00000000;
#endif
}
/**************************************************************************/
/*!
Helper for Energia, it will set CS pin accordly module selected
module: 0...3
[private]
*/
/**************************************************************************/
#if defined(NEEDS_SET_MODULE)
void RA8875::selectCS(uint8_t module)
{
if (module > 3) module = 3;
switch(module){
case 0:
_cs = PA_3;
break;
case 1:
_cs = PF_3;
break;
case 2:
_cs = PB_5;
break;
case 3:
_cs = PD_1;
break;
}
SPImodule = module;
}
#endif
/**************************************************************************/
/*!
Initialize library and SPI
Parameter:
(display type)
RA8875_480x272 (4.3" displays)
RA8875_800x480 (5" and 7" displays)
Adafruit_480x272 (4.3" Adafruit displays)
Adafruit_800x480 (5" and 7" Adafruit displays)
(colors) - The color depth (default 16) 8 or 16 (bit)
-------------------------------------------------------------
UPDATE! in Energia IDE some devices needs an extra parameter!
module: sets the SPI interface (it depends from MCU). Default:0
*/
/**************************************************************************/
void RA8875::begin(const enum RA8875sizes s,uint8_t colors, uint32_t SPIMaxSpeed, uint32_t SPIMaxReadSpeed )
{
_errorCode = 0;
_displaySize = s;
_rotation = 0;
_portrait = false;
_inited = false;
_sleep = false;
_hasLayerLimits = false;
_intPin = 255;
_intNum = 0;
_useISR = false;
_enabledInterrups = 0b00000000;
#if defined(SPI_HAS_TRANSACTION)
_SPIMaxSpeed = SPIMaxSpeed;
_SPIMaxReadSpeed = SPIMaxReadSpeed;
#endif
/* used to understand wat causes an INT
bit
0:
1:
2: Touch (resistive)
3:
4:
5:
6:
7:
8:
*/
#if defined(USE_FT5206_TOUCH)
_intCTSPin = 255;
_intCTSNum = 0;
#endif
#if defined(USE_RA8875_SEPARATE_TEXT_COLOR)
_TXTForeColor = _RA8875_DEFAULTTXTFRGRND;
#if defined(_RA8875_DEFAULTTXTBKGRND)
_TXTBackColor = _RA8875_DEFAULTTXTBKGRND;
#else
_TXTBackColor = 0x0000;
#endif
_TXTrecoverColor = false;
#endif
_maxLayers = 2;
_currentLayer = 0;
_useMultiLayers = false;//starts with one layer only
_textMode = false;
_brightness = 255;
_cursorX = 0; _cursorY = 0; _scrollXL = 0; _scrollXR = 0; _scrollYT = 0; _scrollYB = 0;
_scaleX = 1; _scaleY = 1;
_scaling = false;
_EXTFNTsize = X16;
_FNTspacing = 0;
//_FNTrender = false;
/* set--> _TXTparameters <--
0:_extFontRom = false;
1:_autoAdvance = true;
2:_textWrap = user defined
3:_fontFullAlig = false;
4:_fontRotation = false;//not used
5:_alignXToCenter = false;
6:_alignYToCenter = false;
7: render = false;
*/
_TXTparameters = 0b00000010;
bitWrite(_TXTparameters,2,_DFT_RA8875_TEXTWRAP);//set _textWrap
_relativeCenter = false;
_absoluteCenter = false;
_EXTFNTrom = _DFT_RA8875_EXTFONTROMTYPE;
_EXTFNTcoding = _DFT_RA8875_EXTFONTROMCODING;
_FNTinterline = 0;
_EXTFNTfamily = STANDARD;
_FNTcursorType = NOCURSOR;
_FNTgrandient = false;
_arcAngle_max = ARC_ANGLE_MAX;
_arcAngle_offset = ARC_ANGLE_OFFSET;
_angle_offset = ANGLE_OFFSET;
_color_bpp = 16;
_colorIndex = 0;
if (colors != 16) {
_color_bpp = 8;
_colorIndex = 3;
}
switch (_displaySize){
case RA8875_480x272:
case Adafruit_480x272:
_width = 480;
_height = 272;
_initIndex = 0;
break;
case RA8875_800x480:
case Adafruit_800x480:
case RA8875_800x480ALT:
_width = 800;
_height = 480;
_hasLayerLimits = true;
_maxLayers = 1;
if (_color_bpp < 16) _maxLayers = 2;
_initIndex = 1;
if (_displaySize == RA8875_800x480ALT) _initIndex = 2;
break;
default:
_errorCode |= (1 << 0);//set
return;
}
RA8875_WIDTH = _width;
RA8875_HEIGHT = _height;
_activeWindowXL = 0;
_activeWindowXR = RA8875_WIDTH;
_activeWindowYT = 0;
_activeWindowYB = RA8875_HEIGHT;
//hack
_cursorY = 0;
_cursorY = 0;
textsize = 1;
setTextSize(1);
wrap = true;
font = NULL;
setClipRect();
setOrigin();
#if !defined(_AVOID_TOUCHSCREEN)//common to all touch
_clearTInt = false;
_touchEnabled = false;
#if defined(USE_RA8875_TOUCH)//resistive touch
_touchrcal_xlow = TOUCSRCAL_XLOW; _touchrcal_ylow = TOUCSRCAL_YLOW; _touchrcal_xhigh = TOUCSRCAL_XHIGH; _touchrcal_yhigh = TOUCSRCAL_YHIGH;
_calibrated = _isCalibrated();//check calibration at startup
if (!_calibrated) {
_tsAdcMinX = 0; _tsAdcMinY = 0; _tsAdcMaxX = 1023; _tsAdcMaxY = 1023;
} else {
_tsAdcMinX = TOUCSRCAL_XLOW; _tsAdcMinY = TOUCSRCAL_YLOW; _tsAdcMaxX = TOUCSRCAL_XHIGH; _tsAdcMaxY = TOUCSRCAL_YHIGH;
}
#endif
#endif
#if defined(USE_RA8875_KEYMATRIX)
_keyMatrixEnabled = false;
#endif
/* Display Configuration Register [0x20]
7: (Layer Setting Control) 0:one Layer, 1:two Layers
6,5,4: (na)
3: (Horizontal Scan Direction) 0: SEG0 to SEG(n-1), 1: SEG(n-1) to SEG0
2: (Vertical Scan direction) 0: COM0 to COM(n-1), 1: COM(n-1) to COM0
1,0: (na) */
_DPCR_Reg = 0b00000000;
/* Memory Write Control Register 0 [0x40]
7: 0(graphic mode), 1(textx mode)
6: 0(font-memory cursor not visible), 1(visible)
5: 0(normal), 1(blinking)
4: na
3-2: 00(LR,TB), 01(RL,TB), 10(TB,LR), 11(BT,LR)
1: 0(Auto Increase in write), 1(no)
0: 0(Auto Increase in read), 1(no) */
_MWCR0_Reg = 0b00000000;
/* Font Control Register 0 [0x21]
7: 0(CGROM font is selected), 1(CGRAM font is selected)
6: na
5: 0(Internal CGROM [reg 0x2F to 00]), 1(External CGROM [0x2E reg, bit6,7 to 0)
4-2: na
1-0: 00(ISO/IEC 8859-1), 01(ISO/IEC 8859-2), 10(ISO/IEC 8859-3), 11(ISO/IEC 8859-4)*/
_FNCR0_Reg = 0b00000000;
/* Font Control Register 1 [0x22]
7: 0(Full Alignment off), 1(Full Alignment on)
6: 0(no-trasparent), 1(trasparent)
5: na
4: 0(normal), 1(90degrees)
3-2: 00(x1), 01(x2), 10(x3), 11(x3) Horizontal font scale
1-0: 00(x1), 01(x2), 10(x3), 11(x3) Vertical font scale */
_FNCR1_Reg = 0b00000000;
/* Font Write Type Setting Register [0x2E]
7-6: 00(16x16,8x16,nx16), 01(24x24,12x24,nx24), 1x(32x32,16x32, nx32)
5-0: 00...3F (font width off to 63 pixels)*/
_FWTSET_Reg = 0b00000000;
/* Serial Font ROM Setting [0x2F]
GT Serial Font ROM Select
7-5: 000(GT21L16TW/GT21H16T1W),001(GT30L16U2W),010(GT30L24T3Y/GT30H24T3Y),011(GT30L24M1Z),111(GT30L32S4W/GT30H32S4W)
FONT ROM Coding Setting
4-2: 000(GB2312),001(GB12345/GB18030),010(BIG5),011(UNICODE),100(ASCII),101(UNI-Japanese),110(JIS0208),111(Latin/Greek/Cyrillic/Arabic)
1-0: 00...11
bits ASCII Lat/Gr/Cyr Arabic
00 normal normal na
01 Arial var Wdth Pres Forms A
10 Roman na Pres Forms B
11 Bold na na */
_SFRSET_Reg = 0b00000000;
/* Interrupt Control Register1 [0xF0]
7,6,5: (na)
4: KEYSCAN Interrupt Enable Bit
3: DMA Interrupt Enable Bit
2: TOUCH Panel Interrupt Enable Bit
1: BTE Process Complete Interrupt Enable Bit
0:
When MCU-relative BTE operation is selected(*1) and BTE
Function is Enabled(REG[50h] Bit7 = 1), this bit is used to
Enable the BTE Interrupt for MCU R/W:
0 : Disable BTE interrupt for MCU R/W.
1 : Enable BTE interrupt for MCU R/W.
When the BTE Function is Disabled, this bit is used to
Enable the Interrupt of Font Write Function:
0 : Disable font write interrupt.
1 : Enable font write interrupt.
*/
_INTC1_Reg = 0b00000000;
//------------------------------- Start SPI initialization ------------------------------------------
#if defined(__MK20DX128__) || defined(__MK20DX256__)
//always uses SPI transaction
if ((_mosi == 11 || _mosi == 7) && (_miso == 12 || _miso == 8) && (_sclk == 13 || _sclk == 14)) {//valid SPI pins?
if (_mosi != 11) SPI.setMOSI(_mosi);
if (_miso != 12) SPI.setMISO(_miso);
if (_sclk != 13) SPI.setSCK(_sclk);
} else {
_errorCode |= (1 << 1);//set
return;
}
pinMode(_cs, OUTPUT);
SPI.begin();
digitalWrite(_cs, HIGH);
#elif defined(__MK64FX512__) || defined(__MK66FX1M0__)//future teensys
//always uses SPI transaction
//always uses SPI transaction
if (SPI.pinIsMISO(_miso) && SPI.pinIsMOSI(_mosi) && SPI.pinIsSCK(_sclk)) {
_pspi = &SPI;
if (_mosi != 11) SPI.setMOSI(_mosi);
if (_miso != 12) SPI.setMISO(_miso);
if (_sclk != 13) SPI.setSCK(_sclk);
//Serial.println("Use SPI");
} else if (SPI1.pinIsMISO(_miso) && SPI1.pinIsMOSI(_mosi) && SPI1.pinIsSCK(_sclk)) {
_pspi = &SPI1;
if (_mosi != 0) SPI1.setMOSI(_mosi);
if (_miso != 1) SPI1.setMISO(_miso);
if (_sclk != 32) SPI1.setSCK(_sclk);
//Serial.println("Use SPI1");
} else if (SPI2.pinIsMISO(_miso) && SPI2.pinIsMOSI(_mosi) && SPI2.pinIsSCK(_sclk)) {
_pspi = &SPI2;
if (_mosi != 44) SPI2.setMOSI(_mosi);
if (_miso != 45) SPI2.setMISO(_miso);
if (_sclk != 46) SPI2.setSCK(_sclk);
//Serial.println("Use SPI2");
} else {
_errorCode |= (1 << 1);//set
return;
}
pinMode(_cs, OUTPUT);
_pspi->begin();
digitalWrite(_cs, HIGH);
#elif defined(__IMXRT1062__)
//always uses SPI transaction
if (SPI.pinIsMISO(_miso) && SPI.pinIsMOSI(_mosi) && SPI.pinIsSCK(_sclk)) {
_pspi = &SPI;
} else if (SPI1.pinIsMISO(_miso) && SPI1.pinIsMOSI(_mosi) && SPI1.pinIsSCK(_sclk)) {
_pspi = &SPI1;
} else if (SPI2.pinIsMISO(_miso) && SPI2.pinIsMOSI(_mosi) && SPI2.pinIsSCK(_sclk)) {
_pspi = &SPI2;
} else {
_errorCode |= (1 << 1);//set
return;
}
// Make sure we select the right pins.
// On T4 does nothing, and on T4.1 only miso matters, but just in case.
_pspi->setMISO(_miso);
_pspi->setMOSI(_mosi);
_pspi->setSCK(_sclk);
pinMode(_cs, OUTPUT);
_pspi->begin();
digitalWrite(_cs, HIGH);
#elif defined(__MKL26Z64__)//TeensyLC
//always uses SPI ransaction
#if TEENSYDUINO > 121//not supported prior 1.22!
if (SPI.pinIsMISO(_miso) && SPI.pinIsMOSI(_mosi) && SPI.pinIsSCK(_sclk)) {
_pspi = &SPI;
if (_mosi != 11) SPI.setMOSI(_mosi);
if (_miso != 12) SPI.setMISO(_miso);
if (_sclk != 13) SPI.setSCK(_sclk);
//Serial.println("Use SPI");
} else if (SPI1.pinIsMISO(_miso) && SPI1.pinIsMOSI(_mosi) && SPI1.pinIsSCK(_sclk)) {
_pspi = &SPI1;
if (_mosi != 0) SPI1.setMOSI(_mosi);
if (_miso != 1) SPI1.setMISO(_miso);
if (_sclk != 20) SPI1.setSCK(_sclk);
//Serial.println("Use SPI1");
} else {
_errorCode |= (1 << 1);//set
return;
}
pinMode(_cs, OUTPUT);
_pspi->begin();
digitalWrite(_cs, HIGH);
#else
_pspi = &SPI;
pinMode(_cs, OUTPUT);
SPI.begin();
digitalWrite(_cs, HIGH);
_errorCode |= (1 << 3);//set
#endif
#elif !defined(ENERGIA)//everithing but ENERGIA
#if defined(___DUESTUFF)// DUE
#if defined(SPI_DUE_MODE_EXTENDED)
//DUE SPI mode extended you can use only follow pins
if (_cs == 4 || _cs == 10 || _cs == 52) {
SPI.begin(_cs);
} else {
_errorCode |= (1 << 2);//error! wrong cs pin
return;
}
#else
//DUE in normal SPI mode
pinMode(_cs, OUTPUT);
SPI.begin();
#if defined(_FASTSSPORT)
csport = portOutputRegister(digitalPinToPort(_cs));
cspinmask = digitalPinToBitMask(_cs);
*csport |= cspinmask;//hi
#else
digitalWrite(_cs, HIGH);
#endif
#endif
#elif defined(ESP8266)
pinMode(_cs, OUTPUT);
SPI.begin();
#if defined(_FASTSSPORT)
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, _pinRegister(_cs));//H
#else
digitalWrite(_cs, HIGH);//for now
#endif
#elif defined(SPARK)
pinMode(_cs, OUTPUT);
SPI.begin();
pinSetFast(_cs);//for now
#else
//UNO,MEGA,Yun,nano,duemilanove and other 8 bit arduino's
pinMode(_cs, OUTPUT);
SPI.begin();
csport = portOutputRegister(digitalPinToPort(_cs));//pinMode(_cs, OUTPUT);
cspinmask = digitalPinToBitMask(_cs);
*csport |= cspinmask;//hi
#endif
#endif
if (_rst < 255){//time for hardware reset RA8875
pinMode(_rst, OUTPUT);
digitalWrite(_rst, HIGH);
delay(10);
digitalWrite(_rst, LOW);
delay(220);//120
digitalWrite(_rst, HIGH);
delay(300);//200
}
#if defined(ENERGIA) && defined(NEEDS_SET_MODULE)//energia specific
SPI.setModule(SPImodule);
#endif
//set SPI SPEED, starting at low speed, after init will raise up!
#if defined(SPI_HAS_TRANSACTION)
_SPITransactionSpeed = 4000000UL;//we start in low speed here!
#else//do not use SPItransactons
#if defined (__AVR__)//8 bit arduino's
pinMode(_cs, OUTPUT);
SPI.begin();
SPI.setClockDivider(SPI_SPEED_SAFE);
delay(1);
SPI.setDataMode(SPI_MODE3);
#else
#if defined(___DUESTUFF) && defined(SPI_DUE_MODE_EXTENDED)
SPI.setClockDivider(_cs,SPI_SPEED_SAFE);
delay(1);
SPI.setDataMode(_cs,SPI_MODE3);
#elif defined (ESP8266)
SPI.setClockDivider(SPI_SPEED_SAFE);
delay(1);
SPI.setDataMode(SPI_MODE0);
#elif defined(SPARK)
SPI.setClockDivider(SPI_SPEED_SAFE);
delay(1);
SPI.setDataMode(SPI_MODE0);
#else
SPI.setClockDivider(SPI_SPEED_SAFE);
delay(1);
SPI.setDataMode(SPI_MODE3);
#endif
#endif
#endif
#if defined(ENERGIA)//dunno why but energia wants this here or not work!
pinMode(_cs, OUTPUT);
digitalWrite(_cs, HIGH);
#endif
//SPI initialization done
if (_errorCode != 0) return;//ouch, error/s.Better stop here!
_initialize();//----->Time to Initialize the RA8875!
//------- time for capacitive touch stuff -----------------
#if defined(USE_FT5206_TOUCH)
_wire->begin();
#if defined(___DUESTUFF)
_wire->setClock(400000UL); // Set I2C frequency to 400kHz
/*
#if !defined(USE_DUE_WIRE1_INTERFACE)//sorry but I do not own a DUE, have to learn about Wire1
// Force 400 KHz I2C, rawr! (Uses pins 20, 21 for SDA, SCL)
TWI1->TWI_CWGR = 0;
TWI1->TWI_CWGR = ((VARIANT_MCK / (2 * 400000)) - 4) * 0x101;
#endif
*/
#else
//TODO, Dunno what to do here with SPARK
#if ARDUINO >= 157
_wire->setClock(400000UL); // Set I2C frequency to 400kHz
#else
TWBR = ((F_CPU / 400000UL) - 16) / 2; // Set I2C frequency to 400kHz
#endif
#endif
delay(10);
_initializeFT5206();//initialize FT5206 controller
_maxTouch = 5;
_gesture = 0;
_currentTouches = 0;
_currentTouchState = 0;
_needISRrearm = false;
//TO DO
//Modify RA8875 registers and disconnect internal Touch Screen totally
#endif
if(_displaySize == Adafruit_480x272 || _displaySize == Adafruit_800x480 ) GPIOX(true);
}
/************************* Initialization *********************************/
/**************************************************************************/
/*!
Hardware initialization of RA8875 and turn on
[private]
*/
/**************************************************************************/
void RA8875::_initialize()
{
_inited = false;
// HACK to setup SPI MODE 3
/* SPI.beginTransaction(SPISettings(_SPIMaxSpeed, MSBFIRST, SPI_MODE3));
SPI.transfer(0);
SPI.endTransaction();
delay(1);
*/
if (_rst == 255) {// soft reset time ?
writeCommand(RA8875_PWRR);
_writeData(RA8875_PWRR_SOFTRESET);
delay(20);
_writeData(RA8875_PWRR_NORMAL);
delay(200);
}
//set the sysClock
_setSysClock(initStrings[_initIndex][0],initStrings[_initIndex][1],initStrings[_initIndex][2]);
//color space setup
if (_color_bpp < 16){//256
_writeRegister(RA8875_SYSR,0x00);//256
_colorIndex = 3;
} else {
_writeRegister(RA8875_SYSR,0x0C);//65K
_colorIndex = 0;
}
_writeRegister(RA8875_HDWR,initStrings[_initIndex][3]); //LCD Horizontal Display Width Register
_writeRegister(RA8875_HNDFTR,initStrings[_initIndex][4]); //Horizontal Non-Display Period Fine Tuning Option Register
_writeRegister(RA8875_HNDR,initStrings[_initIndex][5]); //LCD Horizontal Non-Display Period Register
_writeRegister(RA8875_HSTR,initStrings[_initIndex][6]); //HSYNC Start Position Register
_writeRegister(RA8875_HPWR,initStrings[_initIndex][7]); //HSYNC Pulse Width Register
_writeRegister(RA8875_VDHR0,initStrings[_initIndex][8]); //LCD Vertical Display Height Register0
_writeRegister(RA8875_VDHR0+1,initStrings[_initIndex][9]); //LCD Vertical Display Height Register1
_writeRegister(RA8875_VNDR0,initStrings[_initIndex][10]); //LCD Vertical Non-Display Period Register 0
_writeRegister(RA8875_VNDR0+1,initStrings[_initIndex][11]); //LCD Vertical Non-Display Period Register 1
_writeRegister(RA8875_VSTR0,initStrings[_initIndex][12]); //VSYNC Start Position Register 0
_writeRegister(RA8875_VSTR0+1,initStrings[_initIndex][13]); //VSYNC Start Position Register 1
_writeRegister(RA8875_VPWR,initStrings[_initIndex][14]); //VSYNC Pulse Width Register
_updateActiveWindow(true); //set the whole screen as active
//clearActiveWindow();
delay(10); //100
setCursorBlinkRate(DEFAULTCURSORBLINKRATE);//set default blink rate
setIntFontCoding(DEFAULTINTENCODING);//set default internal font encoding
setFont(INTFONT); //set internal font use
//postburner PLL!
_setSysClock(sysClockPar[_initIndex][0],sysClockPar[_initIndex][1],initStrings[_initIndex][2]);
_inited = true;
//from here we will go at high speed!
#if defined(SPI_HAS_TRANSACTION)
if (_SPIMaxSpeed == (uint32_t)-1) {
#if defined(__MKL26Z64__)
_SPIMaxSpeed = (_pspi != &SPI)? MAXSPISPEED2 : MAXSPISPEED;
#else
_SPIMaxSpeed = MAXSPISPEED;
#endif
}
if (_SPIMaxReadSpeed == (uint32_t)-1) {
_SPIMaxReadSpeed = MAXSPIREADSPEED;
}
_SPITransactionSpeed = _SPIMaxSpeed;
//Serial.printf("SPI Transaction speed: %d Max Speed:%d\n", _SPITransactionSpeed, _SPIMaxSpeed);
#endif
#if defined(_FASTCPU)
_slowDownSPI(false);
#else
#if defined(SPI_HAS_TRANSACTION)
#else
#if defined(___DUESTUFF) && defined(SPI_DUE_MODE_EXTENDED)
SPI.setClockDivider(_cs,SPI_SPEED_WRITE);
#else
SPI.setClockDivider(SPI_SPEED_WRITE);
#endif
#endif
#endif
delay(1);
clearMemory();//clearMemory(true);
delay(1);
displayOn(true);//turn On Display
delay(1);
fillWindow(_RA8875_DEFAULTBACKLIGHT);//set screen black
backlight(true);
setRotation(_RA8875_DEFAULTSCRROT);
_setTextMode(false);
setActiveWindow();
#if defined(_RA8875_DEFAULTTXTBKGRND)
#if defined(USE_RA8875_SEPARATE_TEXT_COLOR)
setForegroundColor(_TXTForeColor);
setBackgroundColor(_TXTBackColor);
#else
setForegroundColor(_RA8875_DEFAULTTXTFRGRND);
setBackgroundColor(_RA8875_DEFAULTTXTBKGRND);
#endif
_backTransparent = false;
_FNCR1_Reg &= ~(1 << 6);//clear
#else
#if defined(USE_RA8875_SEPARATE_TEXT_COLOR)
setForegroundColor(_TXTForeColor);
#else
setForegroundColor(_RA8875_DEFAULTTXTFRGRND);
#endif
_backTransparent = true;
_FNCR1_Reg |= (1 << 6);//set
#endif
_writeRegister(RA8875_FNCR1,_FNCR1_Reg);
setCursor(0,0);
}
/**************************************************************************/
/*!
This function set the sysClock accordly datasheet
Parameters:
pll1: PLL Control Register 1
pll2: PLL Control Register 2
pixclk: Pixel Clock Setting Register
[private]
*/
/**************************************************************************/
void RA8875::_setSysClock(uint8_t pll1,uint8_t pll2,uint8_t pixclk)
{
_writeRegister(RA8875_PLLC1,pll1);////PLL Control Register 1
delay(1);
_writeRegister(RA8875_PLLC1+1,pll2);////PLL Control Register 2
delay(1);
_writeRegister(RA8875_PCSR,pixclk);//Pixel Clock Setting Register
delay(1);
}
/**************************************************************************/
/*!
This return a byte with the error code/s:
bit--------------------------------------------------------------------
0: The display it's not supported!
1: The MOSI or MISO or SCLK choosed for Teensy it's out permitted range!
2: The CS pin you selected it's out permitted range!
3: You have to upgrade to Teensyduino 1.22 or better to use this feature!
4:
5:
6:
7:
0b00000000 = no error
*/
/**************************************************************************/
uint8_t RA8875::errorCode(void)
{
return _errorCode;
}
/**************************************************************************/
/*!
return true when register has done the job, otherwise false.
*/
/**************************************************************************/
boolean RA8875::_waitPoll(uint8_t regname, uint8_t waitflag, uint8_t timeout)
{
uint8_t temp;
unsigned long start_time = millis();
while (1) {
temp = _readRegister(regname);
if (!(temp & waitflag)) {
//unsigned long delta_time = millis() - timeout;
//if ((delta_time > 10) || (waitflag == RA8875_DCR_CIRCLE_STATUS)) Serial.printf("+_waitPoll %x %x %d\n", temp, waitflag, delta_time);
return true;
}
if ((millis() - start_time) > timeout) {
//Serial.printf("TO _waitPoll %x %x\n", temp, waitflag);
return false;//emergency exit!
}
}
return false;
}
/**************************************************************************/
/*!
Just another specified wait routine until job it's done
Parameters:
res:
0x80(for most operations),
0x40(BTE wait),
0x01(DMA wait)
*/
/**************************************************************************/
void RA8875::_waitBusy(uint8_t res)
{
uint8_t temp;
unsigned long start = millis();//M.Sandercock
do {
if (res == 0x01) writeCommand(RA8875_DMACR);//dma
temp = readStatus();
if ((millis() - start) > 10) return;
} while ((temp & res) == res);
}
/**************************************************************************/
/*!
Clear memory (different from fillWindow!)
Parameters:
full: true(clear all memory), false(clear active window only)
When in multilayer it automatically clear L1 & L1 and switch back to current layer
*/
/**************************************************************************/
/*
void RA8875::clearMemory(boolean full)
{
uint8_t temp = 0b10000000;
if (!full && !_useMultiLayers) temp |= (1 << 6);//set 6
_writeRegister(RA8875_MCLR,temp);
_waitBusy(0x80);
if (full && _useMultiLayers){
temp = 0b10000000;
uint8_t templ = _currentLayer;//remember current layer
if (templ > 0){//we are in L2
writeTo(L1);//switch to L1
} else {
writeTo(L2);//switch to L2
}
_writeRegister(RA8875_MCLR,temp);
_waitBusy(0x80);
if (templ > 0){//we was in L2
writeTo(L2);//switch back to L2
} else {
writeTo(L1);//switch back to L1
}
}
}
*/
/**************************************************************************/
/*!
Clear memory (different from fillWindow!)
Parameters:
stop: stop clear memory operation
*/
/**************************************************************************/
void RA8875::clearMemory(bool stop)
{
uint8_t temp;
temp = _readRegister(RA8875_MCLR);
stop == true ? temp &= ~(1 << 7) : temp |= (1 << 7);
_writeData(temp);
if (!stop) _waitBusy(0x80);
}
/**************************************************************************/
/*!
Clear the active window
Parameters:
full: false(clear current window), true clear full window
*/
/**************************************************************************/
void RA8875::clearActiveWindow(bool full)
{
uint8_t temp;
temp = _readRegister(RA8875_MCLR);
full == true ? temp &= ~(1 << 6) : temp |= (1 << 6);
_writeData(temp);
//_waitBusy(0x80);
}
/**************************************************************************/
/*!
Clear width BG color
Parameters:
bte: false(clear width BTE BG color), true(clear width font BG color)
*/
/**************************************************************************/
void RA8875::clearWidthColor(bool bte)
{
uint8_t temp;
temp = _readRegister(RA8875_MCLR);
bte == true ? temp &= ~(1 << 0) : temp |= (1 << 0);
_writeData(temp);
//_waitBusy(0x80);
}
/**************************************************************************/
/*!
turn display on/off
*/
/**************************************************************************/
void RA8875::displayOn(boolean on)
{
on == true ? _writeRegister(RA8875_PWRR, RA8875_PWRR_NORMAL | RA8875_PWRR_DISPON) : _writeRegister(RA8875_PWRR, RA8875_PWRR_NORMAL | RA8875_PWRR_DISPOFF);
}
/**************************************************************************/
/*!
Set the Active Window
Parameters:
XL: Horizontal Left
XR: Horizontal Right
YT: Vertical TOP
YB: Vertical Bottom
*/
/**************************************************************************/
void RA8875::setActiveWindow(int16_t XL,int16_t XR ,int16_t YT ,int16_t YB)
{
if (_portrait){ swapvals(XL,YT); swapvals(XR,YB);}
if (XR >= RA8875_WIDTH) XR = RA8875_WIDTH;
if (YB >= RA8875_HEIGHT) YB = RA8875_HEIGHT;
_activeWindowXL = XL; _activeWindowXR = XR;
_activeWindowYT = YT; _activeWindowYB = YB;
_updateActiveWindow(false);
}
/**************************************************************************/
/*!
Set the Active Window as FULL SCREEN
*/
/**************************************************************************/
void RA8875::setActiveWindow(void)
{
_activeWindowXL = 0; _activeWindowXR = RA8875_WIDTH;
_activeWindowYT = 0; _activeWindowYB = RA8875_HEIGHT;
if (_portrait){swapvals(_activeWindowXL,_activeWindowYT); swapvals(_activeWindowXR,_activeWindowYB);}
_updateActiveWindow(true);
}
/**************************************************************************/
/*!
Set the Active Window
Parameters:
XL: Horizontal Left
XR: Horizontal Right
YT: Vertical TOP
YB: Vertical Bottom
*/
/**************************************************************************/
void RA8875::getActiveWindow(int16_t &XL,int16_t &XR ,int16_t &YT ,int16_t &YB)//0.69b24
{
XL = _activeWindowXL; XR = _activeWindowXR;
YT = _activeWindowYT; YB = _activeWindowYB;
}
/**************************************************************************/
/*!
Return the max tft width.
Parameters:
absolute: if true will return the phisical width
*/
/**************************************************************************/
uint16_t RA8875::width(bool absolute) const
{
if (absolute) return RA8875_WIDTH;
return _width;
}
/**************************************************************************/
/*!
Return the max tft height.
Parameters:
absolute: if true will return the phisical height
*/
/**************************************************************************/
uint16_t RA8875::height(bool absolute) const
{
if (absolute) return RA8875_HEIGHT;
return _height;
}
/**************************************************************************/
/*!
Change the mode between graphic and text
Parameters:
m: can be GRAPHIC or TEXT
[private]
*/
/**************************************************************************/
void RA8875::_setTextMode(bool m)
{
if (m == _textMode) return;
writeCommand(RA8875_MWCR0);
//if (m != 0){//text
if (m){//text
_MWCR0_Reg |= (1 << 7);
_textMode = true;
} else {//graph
_MWCR0_Reg &= ~(1 << 7);
_textMode = false;
}
_writeData(_MWCR0_Reg);
}
/**************************************************************************/
/*!
Change the beam scan direction on display
Parameters:
invertH: true(inverted),false(normal) horizontal
invertV: true(inverted),false(normal) vertical
*/
/**************************************************************************/
void RA8875::_scanDirection(boolean invertH,boolean invertV)
{
invertH == true ? _DPCR_Reg |= (1 << 3) : _DPCR_Reg &= ~(1 << 3);
invertV == true ? _DPCR_Reg |= (1 << 2) : _DPCR_Reg &= ~(1 << 2);
_writeRegister(RA8875_DPCR,_DPCR_Reg);
}
/**************************************************************************/
/*!
Change the rotation of the screen
Parameters:
rotation:
0 = default
1 = 90
2 = 180
3 = 270
*/
/**************************************************************************/
void RA8875::setRotation(uint8_t rotation)//0.69b32 - less code
{
_rotation = rotation % 4; //limit to the range 0-3
switch (_rotation) {
case 0:
//default, connector to bottom
_portrait = false;
_scanDirection(0,0);
#if defined(USE_RA8875_TOUCH)
if (!_calibrated) {
_tsAdcMinX = 0; _tsAdcMinY = 0; _tsAdcMaxX = 1023; _tsAdcMaxY = 1023;
} else {
_tsAdcMinX = _touchrcal_xlow; _tsAdcMinY = _touchrcal_ylow; _tsAdcMaxX = _touchrcal_xhigh; _tsAdcMaxY = _touchrcal_yhigh;
}
#endif
break;
case 1:
//90
_portrait = true;
_scanDirection(1,0);
#if defined(USE_RA8875_TOUCH)
if (!_calibrated) {
_tsAdcMinX = 1023; _tsAdcMinY = 0; _tsAdcMaxX = 0; _tsAdcMaxY = 1023;
} else {
_tsAdcMinX = _touchrcal_yhigh; _tsAdcMinY = _touchrcal_xlow; _tsAdcMaxX = _touchrcal_ylow; _tsAdcMaxY = _touchrcal_xhigh;
}
#endif
break;
case 2:
//180
_portrait = false;
_scanDirection(1,1);
#if defined(USE_RA8875_TOUCH)
if (!_calibrated) {
_tsAdcMinX = 1023; _tsAdcMinY = 1023; _tsAdcMaxX = 0; _tsAdcMaxY = 0;
} else {
_tsAdcMinX = _touchrcal_xhigh; _tsAdcMinY = _touchrcal_yhigh; _tsAdcMaxX = _touchrcal_xlow; _tsAdcMaxY = _touchrcal_ylow;
}
#endif
break;
case 3:
//270
_portrait = true;
_scanDirection(0,1);
#if defined(USE_RA8875_TOUCH)
if (!_calibrated) {
_tsAdcMinX = 0; _tsAdcMinY = 1023; _tsAdcMaxX = 1023; _tsAdcMaxY = 0;
} else {
_tsAdcMinX = _touchrcal_ylow; _tsAdcMinY = _touchrcal_xhigh; _tsAdcMaxX = _touchrcal_yhigh; _tsAdcMaxY = _touchrcal_xlow;
}
#endif
break;
}
if (_portrait){
_width = RA8875_HEIGHT;
_height = RA8875_WIDTH;
_FNCR1_Reg |= (1 << 4);
} else {
_width = RA8875_WIDTH;
_height = RA8875_HEIGHT;
_FNCR1_Reg &= ~(1 << 4);
}
_writeRegister(RA8875_FNCR1,_FNCR1_Reg);//0.69b21
setClipRect();
}
/**************************************************************************/
/*!
Get rotation setting
*/
/**************************************************************************/
uint8_t RA8875::getRotation()
{
return _rotation;
}
/**************************************************************************/
/*!
true if rotation 1 or 3
*/
/**************************************************************************/
boolean RA8875::isPortrait(void)
{
return _portrait;
}
/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ TEXT STUFF +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/**************************************************************************/
/*! Upload user custom char or symbol to CGRAM, max 255
Parameters:
symbol[]: an 8bit x 16 char in an array. Must be exact 16 bytes
address: 0...255 the address of the CGRAM where to store the char
*/
/**************************************************************************/
void RA8875::uploadUserChar(const uint8_t symbol[],uint8_t address)
{
uint8_t tempMWCR1 = _readRegister(RA8875_MWCR1);//thanks MorganSandercock
uint8_t i;
if (_textMode) _setTextMode(false);//we are in text mode?
_writeRegister(RA8875_CGSR,address);
writeTo(CGRAM);
writeCommand(RA8875_MRWC);
for (i=0;i<16;i++){
_writeData(symbol[i]);
}
_writeRegister(RA8875_MWCR1, tempMWCR1);//restore register
}
/**************************************************************************/
/*! Retrieve and print to screen the user custom char or symbol
User have to store a custom char before use this function
Parameters:
address: 0...255 the address of the CGRAM where char it's stored
wide:0 for single 8x16 char, if you have wider chars that use
more than a char slot they can be showed combined (see examples)
*/
/**************************************************************************/
void RA8875::showUserChar(uint8_t symbolAddrs,uint8_t wide)
{
if (!_textMode) _setTextMode(true);//we are in graph mode?
uint8_t oldReg1State = _FNCR0_Reg;
uint8_t oldReg2State = 0;
uint8_t i;
oldReg1State |= (1 << 7);//set to CGRAM
oldReg1State |= (1 << 5);//TODO:check this (page 19)
_writeRegister(RA8875_FNCR0,oldReg1State);
if (_scaling){//reset scale (not compatible with this!)
oldReg2State = _FNCR1_Reg;
oldReg2State &= ~(0xF); // clear bits from 0 to 3
_writeRegister(RA8875_FNCR1,oldReg2State);
}
//layers?
if (_useMultiLayers){
if (_currentLayer == 0){
writeTo(L1);
} else {
writeTo(L2);
}
} else {
//writeTo(L1);
}
writeCommand(RA8875_MRWC);
_writeData(symbolAddrs);
if (wide > 0){
for (i=1;i<=wide;i++){
_writeData(symbolAddrs+i);
}
}
if (oldReg2State != 0) _writeRegister(RA8875_FNCR1,_FNCR1_Reg);//put back scale as it was
if (oldReg1State != _FNCR0_Reg) _writeRegister(RA8875_FNCR0,_FNCR0_Reg);//put back state
}
/**************************************************************************/
/*!
Set internal Font Encoding
Parameters:
f: ISO_IEC_8859_1, ISO_IEC_8859_2, ISO_IEC_8859_3, ISO_IEC_8859_4
default: ISO_IEC_8859_1
*/
/**************************************************************************/
void RA8875::setIntFontCoding(enum RA8875fontCoding f)
{
uint8_t temp = _FNCR0_Reg;
temp &= ~((1<<1) | (1<<0));// Clear bits 1 and 0
switch (f){
case ISO_IEC_8859_1:
//do nothing
break;
case ISO_IEC_8859_2:
temp |= (1 << 0);
break;
case ISO_IEC_8859_3:
temp |= (1 << 1);
break;
case ISO_IEC_8859_4:
temp |= ((1<<1) | (1<<0));// Set bits 1 and 0
break;
default:
return;
}
_FNCR0_Reg = temp;
_writeRegister(RA8875_FNCR0,_FNCR0_Reg);
}
/**************************************************************************/
/*!
External Font Rom setup
This will not phisically change the register but should be called before setFont(EXTFONT)!
You should use this values accordly Font ROM datasheet!
Parameters:
ert: ROM Type (GT21L16T1W, GT21H16T1W, GT23L16U2W, GT30H24T3Y, GT23L24T3Y, GT23L24M1Z, GT23L32S4W, GT30H32S4W)
erc: ROM Font Encoding (GB2312, GB12345, BIG5, UNICODE, ASCII, UNIJIS, JIS0208, LATIN)
erf: ROM Font Family (STANDARD, ARIAL, ROMAN, BOLD)
*/
/**************************************************************************/
void RA8875::setExternalFontRom(enum RA8875extRomType ert, enum RA8875extRomCoding erc, enum RA8875extRomFamily erf)
{
if (!_textMode) _setTextMode(true);
_SFRSET_Reg = _readRegister(RA8875_FNCR0);;//just to preserve the reg in case something wrong
uint8_t temp = 0b00000000;
switch(ert){ //type of rom
case GT21L16T1W:
case GT21H16T1W:
temp &= 0x1F;
break;
case GT23L16U2W:
case GT30L16U2W:
case ER3301_1:
temp &= 0x1F; temp |= 0x20;
break;
case GT23L24T3Y:
case GT30H24T3Y:
case ER3303_1://encoding GB12345
temp &= 0x1F; temp |= 0x40;
break;
case GT23L24M1Z:
temp &= 0x1F; temp |= 0x60;
break;
case GT23L32S4W:
case GT30H32S4W:
case GT30L32S4W:
case ER3304_1://encoding GB2312
temp &= 0x1F; temp |= 0x80;
break;
default:
_TXTparameters &= ~(1 << 0);//wrong type, better avoid for future
return;//cannot continue, exit
}
_EXTFNTrom = ert;
switch(erc){ //check rom font coding
case GB2312:
temp &= 0xE3;
break;
case GB12345:
temp &= 0xE3; temp |= 0x04;
break;
case BIG5:
temp &= 0xE3; temp |= 0x08;
break;
case UNICODE:
temp &= 0xE3; temp |= 0x0C;
break;
case ASCII:
temp &= 0xE3; temp |= 0x10;
break;
case UNIJIS:
temp &= 0xE3; temp |= 0x14;
break;
case JIS0208:
temp &= 0xE3; temp |= 0x18;
break;
case LATIN:
temp &= 0xE3; temp |= 0x1C;
break;
default:
_TXTparameters &= ~(1 << 0);//wrong coding, better avoid for future
return;//cannot continue, exit
}
_EXTFNTcoding = erc;
_SFRSET_Reg = temp;
setExtFontFamily(erf,false);
_TXTparameters |= (1 << 0); //bit set 0
//_writeRegister(RA8875_SFRSET,_SFRSET_Reg);//0x2F
//delay(4);
}
void RA8875::fontRomSpeed(uint8_t sp)
{
_writeRegister(0x28,sp);
}
/**************************************************************************/
/*!
select the font family for the external Font Rom Chip
Parameters:
erf: STANDARD, ARIAL, ROMAN, BOLD
setReg:
true(send phisically the register, useful when you change
family after set setExternalFontRom)
false:(change only the register container, useful during config)
NOTE: works only when external font rom it's active
*/
/**************************************************************************/
void RA8875::setExtFontFamily(enum RA8875extRomFamily erf,boolean setReg)
{
if (_FNTsource == EXTFONT) {//only on EXTFONT ROM fonts!
_EXTFNTfamily = erf;
_SFRSET_Reg &= ~(0x03); // clear bits from 0 to 1
switch(erf){ //check rom font family
case STANDARD:
_SFRSET_Reg &= 0xFC;
break;
case ARIAL:
_SFRSET_Reg &= 0xFC; _SFRSET_Reg |= 0x01;
break;
case ROMAN:
_SFRSET_Reg &= 0xFC; _SFRSET_Reg |= 0x02;
break;
case BOLD:
_SFRSET_Reg |= ((1<<1) | (1<<0)); // set bits 1 and 0
break;
default:
_EXTFNTfamily = STANDARD; _SFRSET_Reg &= 0xFC;
return;
}
if (setReg) _writeRegister(RA8875_SFRSET,_SFRSET_Reg);
}
}
/**************************************************************************/
/*!
choose from internal/external (if exist) Font Rom
Parameters:
s: Font source (INTFONT,EXTFONT)
*/
/**************************************************************************/
void RA8875::setFont(enum RA8875fontSource s)
{
_use_int_font = 1;
_use_tfont = 0;
_use_ili_font = 0;
_use_gfx_font = 0;
if (!_textMode) _setTextMode(true);//we are in graph mode?
_TXTparameters &= ~(1 << 7);//render OFF
if (s == INTFONT){
_setFNTdimensions(0);
//check the font coding
if (bitRead(_TXTparameters,0) == 1) {//0.96b22 _extFontRom = true
setFontSize(X16);
_writeRegister(RA8875_SFRSET,0b00000000);//_SFRSET_Reg
}
_FNCR0_Reg &= ~((1<<7) | (1<<5));// Clear bits 7 and 5 (set internal CGROM)
_writeRegister(RA8875_FNCR0,_FNCR0_Reg);
_FNTsource = s;
delay(1);
} else if (s == EXTFONT){
if (bitRead(_TXTparameters,0) == 1) {//0.96b22 _extFontRom = true
_FNTsource = s;
//now switch
_FNCR0_Reg &= ~(1 << 7);//clearBit 7
_FNCR0_Reg |= (1 << 5);//setBit 5
_writeRegister(RA8875_FNCR0,_FNCR0_Reg);//0x21
delay(1);
_writeRegister(RA8875_SFCLR,0x03);//0x02 Serial Flash/ROM CLK frequency/2
setFontSize(X16);
_writeRegister(RA8875_SFRSET,_SFRSET_Reg);//at this point should be already set
delay(4);
_writeRegister(RA8875_SROC,0x28);// 0x28 rom 0,24bit adrs,wave 3,1 byte dummy,font mode, single mode 00101000
delay(4);
} else {
setFont(INTFONT);
_setFNTdimensions(0);
}
} else {
return;
}
_spaceCharWidth = _FNTwidth;
//setFontScale(0);
_scaleX = 1; _scaleY = 1;//reset font scale
}
void RA8875::_setFNTdimensions(uint8_t index)
{
_FNTwidth = fontDimPar[index][0];
_FNTheight = fontDimPar[index][1];
_FNTbaselineLow = fontDimPar[index][2];
_FNTbaselineTop = fontDimPar[index][3];
}
/**************************************************************************/
/*!
choose an external font that will be rendered
Of course slower that internal fonts!
Parameters:
*font: &myfont
*/
/**************************************************************************/
void RA8875::setFont(const tFont *font)
{
_use_tfont = 1;
_use_int_font = 0;
_use_ili_font = 0;
_use_gfx_font = 0;
_currentFont = font;
_FNTheight = _currentFont->font_height;
_FNTwidth = _currentFont->font_width;//if 0 it's variable width font
_FNTcompression = _currentFont->rle;
//get all needed infos
if (_FNTwidth > 0){
_spaceCharWidth = _FNTwidth;
} else {
//_FNTwidth will be 0 to inform other functions that this it's a variable w font
// We just get the space width now...
int temp = _getCharCode(0x20);
if (temp > -1){
#if defined(_FORCE_PROGMEM__)
#if defined(ESP8266)
_spaceCharWidth = FPSTR(&_currentFont->chars[temp].image->image_width);
#else
_spaceCharWidth = PROGMEM_read(&_currentFont->chars[temp].image->image_width);
#endif
#else
_spaceCharWidth = (_currentFont->chars[temp].image->image_width);
#endif
} else {
//font malformed, doesn't have needed space parameter
//will return to system font
setFont(INTFONT);
return;
}
}
_scaleX = 1; _scaleY = 1;//reset font scale
//setFontScale(0);
_TXTparameters |= (1 << 7);//render ON
}
/**************************************************************************/
/*!
Enable/Disable the Font Full Alignemet feature (default off)
Parameters:
align: true,false
Note: not active with rendered fonts
*/
/**************************************************************************/
void RA8875::setFontFullAlign(boolean align)
{
if (bitRead(_TXTparameters,7) == 0){
align == true ? _FNCR1_Reg |= (1 << 7) : _FNCR1_Reg &= ~(1 << 7);
_writeRegister(RA8875_FNCR1,_FNCR1_Reg);
}
}
/**************************************************************************/
/*!
Set distance between text lines (default off)
Parameters:
pix: 0...63 pixels
Note: active with rendered fonts
*/
/**************************************************************************/
void RA8875::setFontInterline(uint8_t pix)
{
if (bitRead(_TXTparameters,7) == 1){
_FNTinterline = pix;
} else {
if (pix > 0x3F) pix = 0x3F;
_FNTinterline = pix;
//_FWTSET_Reg &= 0xC0;
//_FWTSET_Reg |= spc & 0x3F;
_writeRegister(RA8875_FLDR,_FNTinterline);
}
}
/**************************************************************************/
/*!
Set the Text position for write Text only.
Parameters:
x:horizontal in pixels or CENTER(of the screen)
y:vertical in pixels or CENTER(of the screen)
autocenter:center text to choosed x,y regardless text lenght
false: |ABCD
true: AB|CD
NOTE: works with any font
*/
/**************************************************************************/
void RA8875::setCursor(int16_t x, int16_t y,bool autocenter)
{
if (x < 0) x = 0;
if (y < 0) y = 0;
_absoluteCenter = autocenter;
if (_portrait) {//rotation 1,3
if (_use_default) swapvals(x,y);
if (y == CENTER) {//swapped OK
y = _width/2;
if (!autocenter) {
_relativeCenter = true;
_TXTparameters |= (1 << 6);//set x flag
}
}
if (x == CENTER) {//swapped
x = _height/2;
if (!autocenter) {
_relativeCenter = true;
_TXTparameters |= (1 << 5);//set y flag
}
}
} else {//rotation 0,2
if (x == CENTER) {
x = _width/2;
if (!autocenter) {
_relativeCenter = true;
_TXTparameters |= (1 << 5);
}
}
if (y == CENTER) {
y = _height/2;
if (!autocenter) {
_relativeCenter = true;
_TXTparameters |= (1 << 6);
}
}
}
//TODO: This one? Useless?
if (bitRead(_TXTparameters,2) == 0){//textWrap
if (x >= _width) x = _width-1;
if (y >= _height) y = _height-1;
}
_cursorX = x;
_cursorY = y;
//if _relativeCenter or _absoluteCenter do not apply to registers yet!
// Have to go to _textWrite first to calculate the lenght of the entire string and recalculate the correct x,y
if (_relativeCenter || _absoluteCenter) return;
if (bitRead(_TXTparameters,7) == 0) _textPosition(x,y,false);
}
/**************************************************************************/
/*!
Set the x,y position for text only
Parameters:
x: horizontal pos in pixels
y: vertical pos in pixels
update: true track the actual text position internally
note: not active with rendered fonts, just set x,y internal tracked param
[private]
*/
/**************************************************************************/
void RA8875::_textPosition(int16_t x, int16_t y,bool update)
{
#if defined(FORCE_RA8875_TXTREND_FOLLOW_CURS)
_writeRegister(RA8875_F_CURXL,(x & 0xFF));
_writeRegister(RA8875_F_CURXH,(x >> 8));
_writeRegister(RA8875_F_CURYL,(y & 0xFF));
_writeRegister(RA8875_F_CURYH,(y >> 8));
#else
if (bitRead(_TXTparameters,7) == 0){
_writeRegister(RA8875_F_CURXL,(x & 0xFF));
_writeRegister(RA8875_F_CURXH,(x >> 8));
_writeRegister(RA8875_F_CURYL,(y & 0xFF));
_writeRegister(RA8875_F_CURYH,(y >> 8));
}
#endif
if (update){ _cursorX = x; _cursorY = y;}
}
/**************************************************************************/
/*!
Give you back the current text cursor position by reading inside RA8875
Parameters:
x: horizontal pos in pixels
y: vertical pos in pixels
note: works also with rendered fonts
USE: xxx.getCursor(myX,myY);
*/
/**************************************************************************/
void RA8875::getCursor(int16_t &x, int16_t &y)
{
if (bitRead(_TXTparameters,7) == 1) {
getCursorFast(x,y);
} else {
uint8_t t1,t2,t3,t4;
t1 = _readRegister(RA8875_F_CURXL);
t2 = _readRegister(RA8875_F_CURXH);
t3 = _readRegister(RA8875_F_CURYL);
t4 = _readRegister(RA8875_F_CURYH);
x = (t2 << 8) | (t1 & 0xFF);
y = (t4 << 8) | (t3 & 0xFF);
if (_portrait && _use_default) swapvals(x,y);
}
}
/**************************************************************************/
/*!
Give you back the current text cursor position as tracked by library (fast)
Parameters:
x: horizontal pos in pixels
y: vertical pos in pixels
note: works also with rendered fonts
USE: xxx.getCursor(myX,myY);
*/
/**************************************************************************/
void RA8875::getCursorFast(int16_t &x, int16_t &y)
{
x = _cursorX;
y = _cursorY;
if (_portrait && _use_default) swapvals(x,y);
}
int16_t RA8875::getCursorX(void)
{
if (_portrait && _use_default) return _cursorY;
return _cursorX;
}
int16_t RA8875::getCursorY(void)
{
if (_portrait && _use_default) return _cursorX;
return _cursorY;
}
/**************************************************************************/
/*! Show/Hide text cursor
Parameters:
c: cursor type (NOCURSOR,IBEAM,UNDER,BLOCK)
note: not active with rendered fonts
blink: true=blink cursor
*/
/**************************************************************************/
void RA8875::showCursor(enum RA8875tcursor c,bool blink)
{
//uint8_t MWCR1Reg = _readRegister(RA8875_MWCR1) & 0x01;(needed?)
uint8_t cW = 0;
uint8_t cH = 0;
_FNTcursorType = c;
c == NOCURSOR ? _MWCR0_Reg &= ~(1 << 6) : _MWCR0_Reg |= (1 << 6);
if (blink) _MWCR0_Reg |= 0x20;//blink or not?
_writeRegister(RA8875_MWCR0, _MWCR0_Reg);//set cursor
//_writeRegister(RA8875_MWCR1, MWCR1Reg);//close graphic cursor(needed?)
switch (c) {
case IBEAM:
cW = 0x01;
cH = 0x1F;
break;
case UNDER:
cW = 0x07;
cH = 0x01;
break;
case BLOCK:
cW = 0x07;
cH = 0x1F;
break;
case NOCURSOR:
default:
break;
}
//set cursor size
_writeRegister(RA8875_CURHS, cW);
_writeRegister(RA8875_CURVS, cH);
}
/**************************************************************************/
/*! Set cursor property blink and his rate
Parameters:
rate: blink speed (fast 0...255 slow)
note: not active with rendered fonts
*/
/**************************************************************************/
void RA8875::setCursorBlinkRate(uint8_t rate)
{
_writeRegister(RA8875_BTCR,rate);//set blink rate
}
/**************************************************************************/
/*!
set the text color and his background
Parameters:
fcolor: 16bit foreground color (text) RGB565
bcolor: 16bit background color RGB565
NOTE: will set background trasparent OFF
It also works with rendered fonts.
*/
/**************************************************************************/
void RA8875::setTextColor(uint16_t fcolor, uint16_t bcolor)//0.69b30
{
if(_use_ili_font){
//for anti-alias font only
// pre-expand colors for fast alpha-blending later
textcolorPrexpanded = (fcolor | (fcolor << 16)) & 0b00000111111000001111100000011111;
textbgcolorPrexpanded = (bcolor | (bcolor << 16)) & 0b00000111111000001111100000011111;
}
#if defined(USE_RA8875_SEPARATE_TEXT_COLOR)
if (fcolor != _TXTForeColor) {
_TXTForeColor = fcolor;
setForegroundColor(fcolor);
}
if (bcolor != _TXTBackColor) {
_TXTBackColor = bcolor;
setBackgroundColor(bcolor);
}
_backTransparent = false;
#else
_backTransparent = false;
if (fcolor != _foreColor) setForegroundColor(fcolor);
if (bcolor != _backColor) setBackgroundColor(bcolor);
#endif
if (bitRead(_TXTparameters,7) == 0) {
_FNCR1_Reg &= ~(1 << 6);//clear
_writeRegister(RA8875_FNCR1,_FNCR1_Reg);
}
}
/**************************************************************************/
/*!
set the text color w transparent background
Parameters:
fColor: 16bit foreground color (text) RGB565
NOTE: will set background trasparent ON
It also works with rendered fonts.
*/
/**************************************************************************/
void RA8875::setTextColor(uint16_t fcolor)
{
#if defined(USE_RA8875_SEPARATE_TEXT_COLOR)
if (fcolor != _TXTForeColor) {
_TXTForeColor = fcolor;
setForegroundColor(fcolor);
}
_backTransparent = true;
#else
_backTransparent = true;
if (fcolor != _foreColor) setForegroundColor(fcolor);
#endif
if (bitRead(_TXTparameters,7) == 0) {
_FNCR1_Reg |= (1 << 6);//set
_writeRegister(RA8875_FNCR1,_FNCR1_Reg);
}
}
void RA8875::setTextGrandient(uint16_t fcolor1,uint16_t fcolor2)
{
_FNTgrandient = true;
_FNTgrandientColor1 = fcolor1;
_FNTgrandientColor2 = fcolor2;
}
/**************************************************************************/
/*!
Set the Text size by it's multiple. normal should=0, max is 3 (x4) for internal fonts
With Rendered fonts the max scale it's not limited
Parameters:
scale: 0..3 -> 0:normal, 1:x2, 2:x3, 3:x4
*/
/**************************************************************************/
void RA8875::setFontScale(uint8_t scale)
{
setFontScale(scale,scale);
}
/**************************************************************************/
/*!
Set the Text size by it's multiple. normal should=0, max is 3 (x4) for internal fonts
With Rendered fonts the max scale it's not limited
This time you can specify different values for vertical and horizontal
Parameters:
xscale: 0..3 -> 0:normal, 1:x2, 2:x3, 3:x4 for internal fonts - 0...xxx for Rendered Fonts
yscale: 0..3 -> 0:normal, 1:x2, 2:x3, 3:x4 for internal fonts - 0...xxx for Rendered Fonts
*/
/**************************************************************************/
void RA8875::setFontScale(uint8_t xscale,uint8_t yscale)
{
_scaling = false;
if (bitRead(_TXTparameters,7) == 0){
xscale = xscale % 4; //limit to the range 0-3
yscale = yscale % 4; //limit to the range 0-3
_FNCR1_Reg &= ~(0xF); // clear bits from 0 to 3
_FNCR1_Reg |= xscale << 2;
_FNCR1_Reg |= yscale;
_writeRegister(RA8875_FNCR1,_FNCR1_Reg);
}
_scaleX = xscale + 1;
_scaleY = yscale + 1;
if (_scaleX > 1 || _scaleY > 1) _scaling = true;
}
/**************************************************************************/
/*!
Normally at every char the cursor advance by one
You can stop/enable this by using this function
Parameters:
on: true(auto advance - default), false:(stop auto advance)
Note: Inactive with rendered fonts
*/
/**************************************************************************/
void RA8875::cursorIncrement(bool on)
{
if (bitRead(_TXTparameters,7) == 0){
on == true ? _MWCR0_Reg &= ~(1 << 1) : _MWCR0_Reg |= (1 << 1);
bitWrite(_TXTparameters,1,on);
_writeRegister(RA8875_MWCR0,_MWCR0_Reg);
}
}
/**************************************************************************/
/*!
Choose between 16x16(8x16) - 24x24(12x24) - 32x32(16x32)
for External Font ROM
Parameters:
ts: X16,X24,X32
Note: Inactive with rendered fonts
TODO: Modify font size variables accordly font size!
*/
/**************************************************************************/
void RA8875::setFontSize(enum RA8875tsize ts)
{
if (_FNTsource == EXTFONT && bitRead(_TXTparameters,7) == 0) {
switch(ts){
case X16:
_FWTSET_Reg &= 0x3F;
_setFNTdimensions(1);
break;
case X24:
_FWTSET_Reg &= 0x3F; _FWTSET_Reg |= 0x40;
_setFNTdimensions(2);
break;
case X32:
_FWTSET_Reg &= 0x3F; _FWTSET_Reg |= 0x80;
_setFNTdimensions(3);
break;
default:
return;
}
_EXTFNTsize = ts;
_writeRegister(RA8875_FWTSET,_FWTSET_Reg);
}
}
/**************************************************************************/
/*!
return the current width of the font in pixel
If font it's scaled, it will multiply.
It's a fast business since the register it's internally tracked
It can also return the usable rows based on the actual fontWidth
Parameters: inColums (true:returns max colums)
TODO: modded for Rendered Fonts
*/
/**************************************************************************/
uint8_t RA8875::getFontWidth(boolean inColums)
{
uint8_t temp;
if (bitRead(_TXTparameters,7) == 1){
temp = _FNTwidth;
if (temp < 1) return 0; //variable with
} else {
temp = (((_FNCR0_Reg >> 2) & 0x3) + 1) * _FNTwidth;
}
if (inColums){
if (_scaleX < 2) return (_width / temp);
temp = temp * _scaleX;
return (_width / temp);
} else {
if (_scaleX < 2) return temp;
temp = temp * _scaleX;
return temp;
}
}
/**************************************************************************/
/*!
return the current heigh of the font in pixel
If font it's scaled, it will multiply.
It's a fast business since the register it's internally tracked
It can also return the usable rows based on the actual fontHeight
Parameters: inRows (true:returns max rows)
TODO: modded for Rendered Fonts
*/
/**************************************************************************/
uint8_t RA8875::getFontHeight(boolean inRows)
{
uint8_t temp;
if (bitRead(_TXTparameters,7) == 1){
temp = _FNTheight;
} else {
temp = (((_FNCR0_Reg >> 0) & 0x3) + 1) * _FNTheight;
}
if (inRows){
if (_scaleY < 2) return (_height / temp);
temp = temp * _scaleY;
return (_height / temp);
} else {
if (_scaleY < 2) return temp;
temp = temp * _scaleY;
return temp;
}
}
/**************************************************************************/
/*!
Choose space in pixels between chars
Parameters:
spc: 0...63pix (default 0=off)
TODO: modded for Rendered Fonts
*/
/**************************************************************************/
void RA8875::setFontSpacing(uint8_t spc)
{
if (spc > 0x3F) spc = 0x3F;
_FNTspacing = spc;
if (bitRead(_TXTparameters,7) == 0){
_FWTSET_Reg &= 0xC0;
_FWTSET_Reg |= spc & 0x3F;
_writeRegister(RA8875_FWTSET,_FWTSET_Reg);
}
}
/**************************************************************************/
/*! PRIVATE
draw a string
Works for all fonts, internal, ROM, external (render)
*/
/**************************************************************************/
void RA8875::_textWrite(const char* buffer, uint16_t len)
{
uint16_t i;
if (len == 0) len = strlen(buffer);//try get the info from the buffer
if (len == 0) return;//better stop here, the string it's really empty!
bool renderOn = bitRead(_TXTparameters,7);//detect if render fonts active
uint8_t loVOffset = 0;
uint8_t hiVOffset = 0;
uint8_t interlineOffset = 0;
uint16_t fcolor = _foreColor;
uint16_t bcolor = _backColor;
uint16_t strngWidth = 0;
uint16_t strngHeight = 0;
if (!renderOn){
loVOffset = _FNTbaselineLow * _scaleY;//calculate lower baseline
hiVOffset = _FNTbaselineTop * _scaleY;//calculate topline
//now check for offset if using an external fonts rom (RA8875 bug)
if (bitRead(_TXTparameters,0) == 1) interlineOffset = 3 * _scaleY;
}
//_absoluteCenter or _relativeCenter cases...................
//plus calculate the real width & height of the entire text in render mode (not trasparent)
if (_absoluteCenter || _relativeCenter || (renderOn && !_backTransparent)){
strngWidth = _STRlen_helper(buffer,len) * _scaleX;//this calculates the width of the entire text
strngHeight = (_FNTheight * _scaleY) - (loVOffset + hiVOffset);//the REAL heigh
if (_absoluteCenter && strngWidth > 0){//Avoid operations for strngWidth = 0
_absoluteCenter = false;
_cursorX = _cursorX - (strngWidth / 2);
_cursorY = _cursorY - (strngHeight / 2) - hiVOffset;
if (_portrait) swapvals(_cursorX,_cursorY);
} else if (_relativeCenter && strngWidth > 0){//Avoid operations for strngWidth = 0
_relativeCenter = false;
if (bitRead(_TXTparameters,5)) {//X = center
if (!_portrait){
_cursorX = (_width / 2) - (strngWidth / 2);
} else {
_cursorX = (_height / 2) - (strngHeight / 2) - hiVOffset;
}
_TXTparameters &= ~(1 << 5);//reset
}
if (bitRead(_TXTparameters,6)) {//Y = center
if (!_portrait){
_cursorY = (_height / 2) - (strngHeight / 2) - hiVOffset;
} else {
_cursorY = (_width / 2) - (strngWidth / 2);
}
_TXTparameters &= ~(1 << 6);//reset
}
}
//if ((_absoluteCenter || _relativeCenter) && strngWidth > 0){//Avoid operations for strngWidth = 0
if (strngWidth > 0){//Avoid operations for strngWidth = 0
#if defined(FORCE_RA8875_TXTREND_FOLLOW_CURS)
_textPosition(_cursorX,_cursorY,false);
#else
if (!renderOn) _textPosition(_cursorX,_cursorY,false);
#endif
}
}//_absoluteCenter,_relativeCenter,(renderOn && !_backTransparent)
//-----------------------------------------------------------------------------------------------
if (!_textMode && !renderOn) _setTextMode(true);// go to text
if (_textMode && renderOn) _setTextMode(false);// go to graphic
//colored text vars
uint16_t grandientLen = 0;
uint16_t grandientIndex = 0;
uint16_t recoverColor = fcolor;
#if defined(USE_RA8875_SEPARATE_TEXT_COLOR)
if (_textMode && _TXTrecoverColor){
if (_foreColor != _TXTForeColor) {_TXTrecoverColor = false;setForegroundColor(_TXTForeColor);}
if (_backColor != _TXTBackColor) {_TXTrecoverColor = false;setBackgroundColor(_TXTBackColor);}
} else {
fcolor = _TXTForeColor;
bcolor = _TXTBackColor;
}
#endif
if (_FNTgrandient){//coloring text
recoverColor = _TXTForeColor;
for (i=0;i 0) fillRect(_cursorX,_cursorY,strngWidth,strngHeight,_backColor);//bColor
#endif
//Loop trough every char and write them one by one...
for (i=0;i -1){//valid?
int charW = 0;
//get charW and glyph
#if defined(_FORCE_PROGMEM__)
#if defined(ESP8266)
charW = FPSTR(&_currentFont->chars[charIndex].image->image_width);
#if !defined(_RA8875_TXTRNDOPTIMIZER)
const uint8_t * charGlyp = FPSTR(&_currentFont->chars[charIndex].image->data);
#endif
#else
charW = PROGMEM_read(&_currentFont->chars[charIndex].image->image_width);
#if !defined(_RA8875_TXTRNDOPTIMIZER)
const uint8_t * charGlyp = PROGMEM_read(&_currentFont->chars[charIndex].image->data);
#endif
#endif
#else
charW = _currentFont->chars[charIndex].image->image_width;
#if !defined(_RA8875_TXTRNDOPTIMIZER)
const uint8_t * charGlyp = _currentFont->chars[charIndex].image->data;
#endif
#endif
//check if goes out of screen and goes to a new line (if wrap) or just avoid
if (bitRead(_TXTparameters,2)){//wrap?
if (!_portrait && (_cursorX + charW * _scaleX) >= _width){
_cursorX = 0;
_cursorY += (_FNTheight * _scaleY) + _FNTinterline + offset;
} else if (_portrait && (_cursorY + charW * _scaleY) >= _width){
_cursorX += (_FNTheight * _scaleY) + _FNTinterline + offset;
_cursorY = 0;
}
#if defined(FORCE_RA8875_TXTREND_FOLLOW_CURS)
//_textPosition(_cursorX,_cursorY,false);
#endif
} else {
if (_portrait){
if (_cursorY + charW * _scaleY >= _width) return;
} else {
if (_cursorX + charW * _scaleX >= _width) return;
}
}
//test purposes ----------------------------------------------------------------
/*
if (!_portrait){
fillRect(_cursorX,_cursorY,(charW * _scaleX),(_FNTheight * _scaleY),RA8875_YELLOW);//bColor
} else {
fillRect(_cursorY,_cursorX,(charW * _scaleX),(_FNTheight * _scaleY),RA8875_YELLOW);//bColor
}
*/
//-------------------------Actual single char drawing here -----------------------------------
if (!_FNTcompression){
#if defined(_RA8875_TXTRNDOPTIMIZER)
if (!_portrait){
_drawChar_unc(_cursorX,_cursorY,charW,charIndex,fcolor);
} else {
_drawChar_unc(_cursorY,_cursorX,charW,charIndex,fcolor);
}
#else
if (!_portrait){
_drawChar_unc(_cursorX,_cursorY,charW,charGlyp,fcolor,bcolor);
} else {
_drawChar_unc(_cursorY,_cursorX,charW,charGlyp,fcolor,bcolor);
}
#endif
} else {
//TODO
//RLE compressed fonts
}
//add charW to total -----------------------------------------------------
if (!_portrait){
_cursorX += (charW * _scaleX) + _FNTspacing;
} else {
_cursorY += (charW * _scaleX) + _FNTspacing;
}
// #if defined(FORCE_RA8875_TXTREND_FOLLOW_CURS)
// _textPosition(_cursorX,_cursorY,false);
// #endif
}//end valid
}//end char
}
/**************************************************************************/
/*! PRIVATE
Write a single char, only INT and FONT ROM char (internal RA9975 render)
NOTE: It identify correctly println and /n & /r
*/
/**************************************************************************/
void RA8875::_charWrite(const char c,uint8_t offset)
{
bool dtacmd = false;
if (c == 13){//'\r'
//Ignore carriage-return
} else if (c == 10){//'\n'
if (!_portrait){
_cursorX = 0;
_cursorY += (_FNTheight + (_FNTheight * (_scaleY - 1))) + _FNTinterline + offset;
} else {
_cursorX += (_FNTheight + (_FNTheight * (_scaleY - 1))) + _FNTinterline + offset;
_cursorY = 0;
}
_textPosition(_cursorX,_cursorY, false);
dtacmd = false;
} else {
if (!dtacmd){
dtacmd = true;
if (!_textMode) _setTextMode(true);//we are in graph mode?
writeCommand(RA8875_MRWC);
}
_writeData(c);
_waitBusy(0x80);
//update cursor
if (!_portrait){
_cursorX += _FNTwidth;
} else {
_cursorY += _FNTwidth;
}
}
}
/**************************************************************************/
/*! PRIVATE
Search for glyph char code in font array
It return font index or -1 if not found.
*/
/**************************************************************************/
int RA8875::_getCharCode(uint8_t ch)
{
int i;
for (i=0;i<_currentFont->length;i++){//search for char code
#if defined(_FORCE_PROGMEM__)
uint8_t ccode = _currentFont->chars[i].char_code;
if (ccode == ch) return i;
#else
if (_currentFont->chars[i].char_code == ch) return i;
#endif
}//i
return -1;
}
/**************************************************************************/
/*! PRIVATE
This helper loop trough a text string and return how long is (in pixel)
NOTE: It identify correctly println and /n & /r and forget non present chars
*/
/**************************************************************************/
int16_t RA8875::_STRlen_helper(const char* buffer,uint16_t len)
{
if (bitRead(_TXTparameters,7) == 0){ //_renderFont not active
return (len * _FNTwidth);
} else { //_renderFont active
int charIndex = -1;
uint16_t i;
if (len == 0) len = strlen(buffer); //try to get data from string
if (len == 0) return 0; //better stop here
if (_FNTwidth > 0){ // fixed width font
return ((len * _spaceCharWidth));
} else { // variable width, need to loop trough entire string!
uint16_t totW = 0;
for (i = 0;i < len;i++){ //loop trough buffer
if (buffer[i] == 32){ //a space
totW += _spaceCharWidth;
} else if (buffer[i] != 13 && buffer[i] != 10 && buffer[i] != 32){//avoid special char
charIndex = _getCharCode(buffer[i]);
if (charIndex > -1) { //found!
#if defined(_FORCE_PROGMEM__)
#if defined(ESP8266)
totW += (FPSTR(&_currentFont->chars[charIndex].image->image_width));
#else
totW += (PROGMEM_read(&_currentFont->chars[charIndex].image->image_width));
#endif
#else
totW += (_currentFont->chars[charIndex].image->image_width);
#endif
}
}//inside permitted chars
}//buffer loop
return totW; //return data
}//end variable w font
}
}
#if defined(_RA8875_TXTRNDOPTIMIZER)
/**************************************************************************/
/*! PRIVATE
Here's the char render engine for uncompressed fonts, it actually render a single char.
It's actually 2 functions, this one take care of every glyph line
and perform some optimization second one paint concurrent pixels in chunks.
To show how optimizations works try uncomment RA8875_VISPIXDEBUG in settings.
Please do not steal this part of code!
*/
/**************************************************************************/
void RA8875::_drawChar_unc(int16_t x,int16_t y,int charW,int index,uint16_t fcolor)
{
//start by getting some glyph data...
#if defined(_FORCE_PROGMEM__)
#if defined(ESP8266)
const uint8_t * charGlyp = FPSTR(&_currentFont->chars[index].image->data);//char data
int totalBytes = FPSTR(&_currentFont->chars[index].image->image_datalen);
#else
const uint8_t * charGlyp = PROGMEM_read(&_currentFont->chars[index].image->data);//char data
int totalBytes = PROGMEM_read(&_currentFont->chars[index].image->image_datalen);
#endif
#else
const uint8_t * charGlyp = _currentFont->chars[index].image->data;
int totalBytes = _currentFont->chars[index].image->image_datalen;
#endif
int i;
uint8_t temp = 0;
//some basic variable...
uint8_t currentXposition = 0;//the current position of the writing cursor in the x axis, from 0 to charW
uint8_t currentYposition = 1;//the current position of the writing cursor in the y axis, from 1 to _FNTheight
int currentByte = 0;//the current byte in reading (from 0 to totalBytes)
bool lineBuffer[charW];//the temporary line buffer (will be _FNTheight each char)
int lineChecksum = 0;//part of the optimizer
/*
uint8_t bytesInLine = 0;
//try to understand how many bytes in a line
if (charW % 8 == 0) { // divisible by 8
bytesInLine = charW / 8;
} else { // when it's divisible by 8?
bytesInLine = charW;
while (bytesInLine % 8) { bytesInLine--;}
bytesInLine = bytesInLine / 8;
}
*/
//the main loop that will read all bytes of the glyph
while (currentByte < totalBytes){
//read n byte
#if defined(_FORCE_PROGMEM__)
#if defined(ESP8266)
temp = FPSTR(&charGlyp[currentByte]);
#else
temp = PROGMEM_read(&charGlyp[currentByte]);
#endif
#else
temp = charGlyp[currentByte];
#endif
for (i=7; i>=0; i--){
//----------------------------------- exception
if (currentXposition >= charW){
//line buffer has been filled!
currentXposition = 0;//reset the line x position
if (lineChecksum < 1){//empty line
#if defined(RA8875_VISPIXDEBUG)
drawRect(x,y + (currentYposition * _scaleY),charW * _scaleX,_scaleY,RA8875_BLUE);
#endif
} else if (lineChecksum == charW){//full line
#if !defined(RA8875_VISPIXDEBUG)
fillRect(
#else
drawRect(
#endif
x,y + (currentYposition * _scaleY),charW * _scaleX,_scaleY,fcolor);
} else { //line render
_charLineRender(lineBuffer,charW,x,y,currentYposition,fcolor);
}
currentYposition++;//next line
lineChecksum = 0;//reset checksum
}//end exception
//-------------------------------------------------------
lineBuffer[currentXposition] = bitRead(temp,i);//continue fill line buffer
lineChecksum += lineBuffer[currentXposition];
currentXposition++;
}
currentByte++;
}
}
/**************************************************************************/
/*! PRIVATE
Font Line render optimized routine
This will render ONLY a single font line by grouping chunks of same pixels
Version 3.0 (fixed a bug that cause xlinePos to jump of 1 pixel
*/
/**************************************************************************/
void RA8875::_charLineRender(bool lineBuffer[],int charW,int16_t x,int16_t y,int16_t currentYposition,uint16_t fcolor)
{
int xlinePos = 0;
int px;
uint8_t endPix = 0;
bool refPixel = false;
while (xlinePos < charW){
refPixel = lineBuffer[xlinePos];//xlinePos pix as reference value for next pixels
//detect and render concurrent pixels
for (px = xlinePos;px <= charW;px++){
if (lineBuffer[px] == lineBuffer[xlinePos] && px < charW){//grouping pixels with same val
endPix++;
} else {
if (refPixel){
#if defined(RA8875_VISPIXDEBUG)
drawRect(
#else
fillRect(
#endif
x,y + (currentYposition * _scaleY),endPix * _scaleX,_scaleY,fcolor);
} else {
#if defined(RA8875_VISPIXDEBUG)
drawRect(x,y + (currentYposition * _scaleY),endPix * _scaleX,_scaleY,RA8875_BLUE);
#endif
}
//reset and update some vals
xlinePos += endPix;
x += endPix * _scaleX;
endPix = 0;
break;//exit cycle for...
}
}
}
}
#else
/**************************************************************************/
/*!
This is the old rendering engine, pretty basic but slow, here for an alternative.
Note that this can be enabled only by commenting
#define _RA8875_TXTRNDOPTIMIZER
in RA8875UserSettings.h file!
Parameters:
x:
Y:
w: the width of the font
data: the data glyph
fcolor: foreground color
bcolor: background color
*/
/**************************************************************************/
void RA8875::_drawChar_unc(int16_t x,int16_t y,int16_t w,const uint8_t *data,uint16_t fcolor,uint16_t bcolor)
{
// if ((x >= _width) || // Clip right
// (y >= _height) || // Clip bottom
// ((x + w * _FNTscaleX - 1) < 0) || // Clip left
// ((y + _FNTheight * _FNTscaleY - 1) < 0)) // Clip top
// return;
uint16_t color;
uint16_t bitCount = 0;
uint8_t line = 0;
int j;
uint16_t i;//,idx;
for (i=0; i<_FNTheight; i++) { //Y
for (j = 0; j 8) {//65K
_color_bpp = 16;
_colorIndex = 0;
_writeRegister(RA8875_SYSR,0x0C);
if (_hasLayerLimits) _maxLayers = 1;
_currentLayer = 0;
}
}
}
/**************************************************************************/
/*!
Return current Color Space (8 or 16)
*/
/**************************************************************************/
uint8_t RA8875::getColorBpp(void)
{
return _color_bpp;
}
/**************************************************************************/
/*!
Sets set the foreground color using 16bit RGB565 color
It handles automatically color conversion when in 8 bit!
Parameters:
color: 16bit color RGB565
*/
/**************************************************************************/
void RA8875::setForegroundColor(uint16_t color)
{
_foreColor = color;//keep track
_writeRegister(RA8875_FGCR0,((color & 0xF800) >> _RA8875colorMask[_colorIndex]));
_writeRegister(RA8875_FGCR0+1,((color & 0x07E0) >> _RA8875colorMask[_colorIndex+1]));
_writeRegister(RA8875_FGCR0+2,((color & 0x001F) >> _RA8875colorMask[_colorIndex+2]));
}
/**************************************************************************/
/*!
Sets set the foreground color using 8bit R,G,B
Parameters:
R: 8bit RED
G: 8bit GREEN
B: 8bit BLUE
*/
/**************************************************************************/
void RA8875::setForegroundColor(uint8_t R,uint8_t G,uint8_t B)
{
_foreColor = Color565(R,G,B);//keep track
_writeRegister(RA8875_FGCR0,R);
_writeRegister(RA8875_FGCR0+1,G);
_writeRegister(RA8875_FGCR0+2,B);
}
/**************************************************************************/
/*!
Sets set the background color using 16bit RGB565 color
It handles automatically color conversion when in 8 bit!
Parameters:
color: 16bit color RGB565
Note: will set background Trasparency OFF
*/
/**************************************************************************/
void RA8875::setBackgroundColor(uint16_t color)
{
_backColor = color;//keep track
_writeRegister(RA8875_BGCR0,((color & 0xF800) >> _RA8875colorMask[_colorIndex]));//11
_writeRegister(RA8875_BGCR0+1,((color & 0x07E0) >> _RA8875colorMask[_colorIndex+1]));//5
_writeRegister(RA8875_BGCR0+2,((color & 0x001F) >> _RA8875colorMask[_colorIndex+2]));//0
}
/**************************************************************************/
/*!
Sets set the background color using 8bit R,G,B
Parameters:
R: 8bit RED
G: 8bit GREEN
B: 8bit BLUE
Note: will set background Trasparency OFF
*/
/**************************************************************************/
void RA8875::setBackgroundColor(uint8_t R,uint8_t G,uint8_t B)
{
_backColor = Color565(R,G,B);//keep track
_writeRegister(RA8875_BGCR0,R);
_writeRegister(RA8875_BGCR0+1,G);
_writeRegister(RA8875_BGCR0+2,B);
}
/**************************************************************************/
/*!
Sets set the trasparent background color using 16bit RGB565 color
It handles automatically color conversion when in 8 bit!
Parameters:
color: 16bit color RGB565
Note: will set background Trasparency ON
*/
/**************************************************************************/
void RA8875::setTransparentColor(uint16_t color)
{
_backColor = color;
_writeRegister(RA8875_BGTR0,((color & 0xF800) >> _RA8875colorMask[_colorIndex]));
_writeRegister(RA8875_BGTR0+1,((color & 0x07E0) >> _RA8875colorMask[_colorIndex+1]));
_writeRegister(RA8875_BGTR0+2,((color & 0x001F) >> _RA8875colorMask[_colorIndex+2]));
}
/**************************************************************************/
/*!
Sets set the Trasparent background color using 8bit R,G,B
Parameters:
R: 8bit RED
G: 8bit GREEN
B: 8bit BLUE
Note: will set background Trasparency ON
*/
/**************************************************************************/
void RA8875::setTransparentColor(uint8_t R,uint8_t G,uint8_t B)
{
_backColor = Color565(R,G,B);//keep track
_writeRegister(RA8875_BGTR0,R);
_writeRegister(RA8875_BGTR0+1,G);
_writeRegister(RA8875_BGTR0+2,B);
}
/**************************************************************************/
/*!
set foreground,background color (plus transparent background)
Parameters:
fColor: 16bit foreground color (text) RGB565
bColor: 16bit background color RGB565
backTransp:if true the bColor will be transparent
*/
/**************************************************************************/
void RA8875::setColor(uint16_t fcolor,uint16_t bcolor,bool bcolorTraspFlag)//0.69b30
{
if (fcolor != _foreColor) setForegroundColor(fcolor);
if (bcolorTraspFlag){
setTransparentColor(bcolor);
} else {
if (bcolor != _backColor) setBackgroundColor(bcolor);
}
}
/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ DRAW STUFF +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/**************************************************************************/
/*! Set graphic cursor beween 8 different ones.
Graphic cursors has to be inserted before use!
Parameters:
cur: 0...7
*/
/**************************************************************************/
void RA8875::setGraphicCursor(uint8_t cur)
{
if (cur > 7) cur = 7;
uint8_t temp = _readRegister(RA8875_MWCR1);
temp &= ~(0x70);//clear bit 6,5,4
temp |= cur << 4;
temp |= cur;
if (_useMultiLayers){
_currentLayer == 1 ? temp |= (1 << 0) : temp &= ~(1 << 0);
} else {
temp &= ~(1 << 0);
}
_writeData(temp);
}
/**************************************************************************/
/*! Show the graphic cursor
Graphic cursors has to be inserted before use!
Parameters:
cur: true,false
*/
/**************************************************************************/
void RA8875::showGraphicCursor(boolean cur)
{
uint8_t temp = _readRegister(RA8875_MWCR1);
cur == true ? temp |= (1 << 7) : temp &= ~(1 << 7);
if (_useMultiLayers){
_currentLayer == 1 ? temp |= (1 << 0) : temp &= ~(1 << 0);
} else {
temp &= ~(1 << 0);
}
_writeData(temp);
}
/**************************************************************************/
/*!
Set the position for Graphic Write
Parameters:
x: horizontal position
y: vertical position
*/
/**************************************************************************/
void RA8875::setXY(int16_t x, int16_t y)
{
setX(x);
setY(y);
}
/**************************************************************************/
/*!
Set the x position for Graphic Write
Parameters:
x: horizontal position
*/
/**************************************************************************/
void RA8875::setX(int16_t x)
{
if (x < 0) x = 0;
if (_portrait){//fix 0.69b21
if (x >= RA8875_HEIGHT) x = RA8875_HEIGHT-1;
_writeRegister(RA8875_CURV0, x & 0xFF);
_writeRegister(RA8875_CURV0+1, x >> 8);
} else {
if (x >= RA8875_WIDTH) x = RA8875_WIDTH-1;
_writeRegister(RA8875_CURH0, x & 0xFF);
_writeRegister(RA8875_CURH0+1, (x >> 8));
}
}
/**************************************************************************/
/*!
Set the y position for Graphic Write
Parameters:
y: vertical position
*/
/**************************************************************************/
void RA8875::setY(int16_t y)
{
if (y < 0) y = 0;
if (_portrait){//fix 0.69b21
if (y >= RA8875_WIDTH) y = RA8875_WIDTH-1;
_writeRegister(RA8875_CURH0, y & 0xFF);
_writeRegister(RA8875_CURH0+1, (y >> 8));
} else {
if (y >= RA8875_HEIGHT) y = RA8875_HEIGHT-1;
_writeRegister(RA8875_CURV0, y & 0xFF);
_writeRegister(RA8875_CURV0+1, y >> 8);
}
}
/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ SCROLL STUFF +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/**************************************************************************/
/*!
Sets the scroll mode. This is controlled by bits 6 and 7 of
REG[52h] Layer Transparency Register0 (LTPR0)
Author: The Experimentalist
*/
/**************************************************************************/
void RA8875::setScrollMode(enum RA8875scrollMode mode)
{
uint8_t temp = _readRegister(RA8875_LTPR0);
temp &= 0x3F; // Clear bits 6 and 7 to zero
switch(mode){ // bit 7,6 of LTPR0
case SIMULTANEOUS: // 00b : Layer 1/2 scroll simultaneously.
// Do nothing
break;
case LAYER1ONLY: // 01b : Only Layer 1 scroll.
temp |= 0x40;
break;
case LAYER2ONLY: // 10b : Only Layer 2 scroll.
temp |= 0x80;
break;
case BUFFERED: // 11b: Buffer scroll (using Layer 2 as scroll buffer)
temp |= 0xC0;
break;
default:
return; //do nothing
}
//TODO: Should this be conditional on multi layer?
//if (_useMultiLayers) _writeRegister(RA8875_LTPR0,temp);
//_writeRegister(RA8875_LTPR0,temp);
_writeData(temp);
}
/**************************************************************************/
/*!
Define a window for perform scroll
Parameters:
XL: x window start left
XR: x window end right
YT: y window start top
YB: y window end bottom
*/
/**************************************************************************/
void RA8875::setScrollWindow(int16_t XL,int16_t XR ,int16_t YT ,int16_t YB)
{
if (_portrait){//0.69b22 (fixed)
swapvals(XL,YT);
swapvals(XR,YB);
}
_checkLimits_helper(XL,YT);
_checkLimits_helper(XR,YB);
_scrollXL = XL; _scrollXR = XR; _scrollYT = YT; _scrollYB = YB;
_writeRegister(RA8875_HSSW0,(_scrollXL & 0xFF));
_writeRegister(RA8875_HSSW0+1,(_scrollXL >> 8));
_writeRegister(RA8875_HESW0,(_scrollXR & 0xFF));
_writeRegister(RA8875_HESW0+1,(_scrollXR >> 8));
_writeRegister(RA8875_VSSW0,(_scrollYT & 0xFF));
_writeRegister(RA8875_VSSW0+1,(_scrollYT >> 8));
_writeRegister(RA8875_VESW0,(_scrollYB & 0xFF));
_writeRegister(RA8875_VESW0+1,(_scrollYB >> 8));
delay(1);
}
/**************************************************************************/
/*!
Perform the scroll
*/
/**************************************************************************/
void RA8875::scroll(int16_t x,int16_t y)
{
if (_portrait) swapvals(x,y);
//if (y > _scrollYB) y = _scrollYB;//??? mmmm... not sure
if (_scrollXL == 0 && _scrollXR == 0 && _scrollYT == 0 && _scrollYB == 0){
//do nothing, scroll window inactive
} else {
_writeRegister(RA8875_HOFS0,(x & 0xFF));
_writeRegister(RA8875_HOFS1,(x >> 8));
_writeRegister(RA8875_VOFS0,(y & 0xFF));
_writeRegister(RA8875_VOFS1,(y >> 8));
}
}
/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ DMA STUFF +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/**************************************************************************/
/*!
*/
/**************************************************************************/
void RA8875::DMA_blockModeSize(int16_t BWR,int16_t BHR,int16_t SPWR)
{
_writeRegister(RA8875_DTNR0,BWR & 0xFF);
_writeRegister(RA8875_BWR1,BWR >> 8);
_writeRegister(RA8875_DTNR1,BHR & 0xFF);
_writeRegister(RA8875_BHR1,BHR >> 8);
_writeRegister(RA8875_DTNR2,SPWR & 0xFF);
_writeRegister(RA8875_SPWR1,SPWR >> 8);
}
/**************************************************************************/
/*!
*/
/**************************************************************************/
void RA8875::DMA_startAddress(unsigned long adrs)
{
_writeRegister(RA8875_SSAR0,adrs & 0xFF);
_writeRegister(RA8875_SSAR0+1,adrs >> 8);
_writeRegister(RA8875_SSAR0+2,adrs >> 16);
//_writeRegister(0xB3,adrs >> 24);// not more in datasheet!
}
/**************************************************************************/
/*!
*/
/**************************************************************************/
void RA8875::DMA_enable(void)
{
uint8_t temp = _readRegister(RA8875_DMACR);
temp |= 0x01;
_writeData(temp);
_waitBusy(0x01);
}
/**************************************************************************/
/*! (STILL IN DEVELOP, please do not complain)
Display an image stored in Flash RAM
Note: you should have the optional FLASH Chip connected to RA8875!
Note: You should store some image in that chip!
Note: Never tried!!!!!!!
*/
/**************************************************************************/
void RA8875::drawFlashImage(int16_t x,int16_t y,int16_t w,int16_t h,uint8_t picnum)
{
if (_portrait){swapvals(x,y); swapvals(w,h);}//0.69b21 -have to check this, not verified
if (_textMode) _setTextMode(false);//we are in text mode?
_writeRegister(RA8875_SFCLR,0x00);
_writeRegister(RA8875_SROC,0x87);
_writeRegister(RA8875_DMACR,0x02);
//setActiveWindow(0,_width-1,0,_height-1);
_checkLimits_helper(x,y);
_checkLimits_helper(w,h);
_portrait == true ? setXY(y,x) : setXY(x,y);
DMA_startAddress(261120 * (picnum-1));
DMA_blockModeSize(w,h,w);
_writeRegister(RA8875_DMACR,0x03);
_waitBusy(0x01);
}
/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ BTE STUFF +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/**************************************************************************/
/*
Block Transfer Move
Can move a rectangular block from any area of memory (eg. layer 1) to any other (eg layer 2)
Can move with transparency - note THE TRANSPARENT COLOUR IS THE TEXT FOREGROUND COLOUR
ReverseDir is for moving overlapping areas - may need to use reverse to prevent it double-copying the overlapping area (this option not available with transparency or monochrome)
ROP is Raster Operation. Usually use RA8875_ROP_SOURCE but a few others are defined
Defaults to current layer if not given or layer is zero.
Monochrome uses the colour-expansion mode: the input is a bit map which is then converted to the current foreground and background colours, transparent background is optional
Monochrome data is assumed to be linear, originally written to the screen memory in 16-bit chunks with drawPixels().
Monochrome mode uses the ROP to define the offset of the first image bit within the first byte. This also depends on the width of the block you are trying to display.
Monochrome skips 16-bit words in the input pattern - see the example for more explanation and a trick to interleave 2 characters in the space of one.
This function returns immediately but the actual transfer can take some time
Caller should check the busy status before issuing any more RS8875 commands.
Basic usage:
BTE_Move(SourceX, SourceY, Width, Height, DestX, DestY) = copy something visible on the current layer
BTE_Move(SourceX, SourceY, Width, Height, DestX, DestY, 2) = copy something from layer 2 to the current layer
BTE_Move(SourceX, SourceY, Width, Height, DestX, DestY, 2, 1, true) = copy from layer 2 to layer 1, with the transparency option
BTE_Move(SourceX, SourceY, Width, Height, DestX, DestY, 0, 0, true, RA8875_BTEROP_ADD) = copy on the current layer, using transparency and the ADD/brighter operation
BTE_Move(SourceX, SourceY, Width, Height, DestX, DestY, 0, 0, false, RA8875_BTEROP_SOURCE, false, true) = copy on the current layer using the reverse direction option for overlapping areas
*/
void RA8875::BTE_move(int16_t SourceX, int16_t SourceY, int16_t Width, int16_t Height, int16_t DestX, int16_t DestY, uint8_t SourceLayer, uint8_t DestLayer,bool Transparent, uint8_t ROP, bool Monochrome, bool ReverseDir)
{
if (SourceLayer == 0) SourceLayer = _currentLayer;
if (DestLayer == 0) DestLayer = _currentLayer;
if (SourceLayer == 2) SourceY |= 0x8000; //set the high bit of the vertical coordinate to indicate layer 2
if (DestLayer == 2) DestY |= 0x8000; //set the high bit of the vertical coordinate to indicate layer 2
ROP &= 0xF0; //Ensure the lower bits of ROP are zero
if (Transparent) {
if (Monochrome) {
ROP |= 0x0A; //colour-expand transparent
} else {
ROP |= 0x05; //set the transparency option
}
} else {
if (Monochrome) {
ROP |= 0x0B; //colour-expand normal
} else {
if (ReverseDir) {
ROP |= 0x03; //set the reverse option
} else {
ROP |= 0x02; //standard block-move operation
}
}
}
_waitBusy(0x40); //Check that another BTE operation is not still in progress
if (_textMode) _setTextMode(false);//we are in text mode?
BTE_moveFrom(SourceX,SourceY);
BTE_size(Width,Height);
BTE_moveTo(DestX,DestY);
BTE_ropcode(ROP);
//Execute BTE! (This selects linear addressing mode for the monochrome source data)
if (Monochrome) _writeRegister(RA8875_BECR0, 0xC0); else _writeRegister(RA8875_BECR0, 0x80);
_waitBusy(0x40);
//we are supposed to wait for the thing to become unbusy
//caller can call _waitBusy(0x40) to check the BTE busy status (except it's private)
}
/**************************************************************************/
/*! TESTING
*/
/**************************************************************************/
void RA8875::BTE_size(int16_t w, int16_t h)
{
//0.69b21 -have to check this, not verified
if (_portrait) swapvals(w,h);
_writeRegister(RA8875_BEWR0,w & 0xFF);//BET area width literacy
_writeRegister(RA8875_BEWR0+1,w >> 8);//BET area width literacy
_writeRegister(RA8875_BEHR0,h & 0xFF);//BET area height literacy
_writeRegister(RA8875_BEHR0+1,h >> 8);//BET area height literacy
}
/**************************************************************************/
/*!
*/
/**************************************************************************/
void RA8875::BTE_moveFrom(int16_t SX,int16_t SY)
{
if (_portrait) swapvals(SX,SY);
_writeRegister(RA8875_HSBE0,SX & 0xFF);
_writeRegister(RA8875_HSBE0+1,SX >> 8);
_writeRegister(RA8875_VSBE0,SY & 0xFF);
_writeRegister(RA8875_VSBE0+1,SY >> 8);
}
/**************************************************************************/
/*!
*/
/**************************************************************************/
void RA8875::BTE_moveTo(int16_t DX,int16_t DY)
{
if (_portrait) swapvals(DX,DY);
_writeRegister(RA8875_HDBE0,DX & 0xFF);
_writeRegister(RA8875_HDBE0+1,DX >> 8);
_writeRegister(RA8875_VDBE0,DY & 0xFF);
_writeRegister(RA8875_VDBE0+1,DY >> 8);
}
/**************************************************************************/
/*! TESTING
Use a ROP code EFX
*/
/**************************************************************************/
void RA8875::BTE_ropcode(unsigned char setx)
{
_writeRegister(RA8875_BECR1,setx);//BECR1
}
/**************************************************************************/
/*! TESTING
Enable BTE transfer
*/
/**************************************************************************/
void RA8875::BTE_enable(bool on)
{
uint8_t temp = _readRegister(RA8875_BECR0);
on == true ? temp &= ~(1 << 7) : temp |= (1 << 7);
_writeData(temp);
//_writeRegister(RA8875_BECR0,temp);
_waitBusy(0x40);
}
/**************************************************************************/
/*! TESTING
Select BTE mode (CONT (continuous) or RECT)
*/
/**************************************************************************/
void RA8875::BTE_dataMode(enum RA8875btedatam m)
{
uint8_t temp = _readRegister(RA8875_BECR0);
m == CONT ? temp &= ~(1 << 6) : temp |= (1 << 6);
_writeData(temp);
//_writeRegister(RA8875_BECR0,temp);
}
/**************************************************************************/
/*! TESTING
Select the BTE SOURCE or DEST layer (1 or 2)
*/
/**************************************************************************/
void RA8875::BTE_layer(enum RA8875btelayer sd,uint8_t l)
{
uint8_t temp;
sd == SOURCE ? temp = _readRegister(RA8875_VSBE0+1) : temp = _readRegister(RA8875_VDBE0+1);
l == 1 ? temp &= ~(1 << 7) : temp |= (1 << 7);
_writeData(temp);
//_writeRegister(RA8875_VSBE1,temp);
}
/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ LAYER STUFF +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/**************************************************************************/
/*!
Instruct the RA8875 chip to use 2 layers
If resolution bring to restrictions it will switch to 8 bit
so you can always use layers.
Parameters:
on: true (enable multiple layers), false (disable)
*/
/**************************************************************************/
void RA8875::useLayers(boolean on)
{
if (_useMultiLayers == on) return; //no reason to do change that it's already as desidered.
//bool clearBuffer = false;
if (_hasLayerLimits && _color_bpp > 8) { //try to set up 8bit color space
setColorBpp(8);
_waitBusy();
_maxLayers = 2;
}
if (on){
_useMultiLayers = true;
_DPCR_Reg |= (1 << 7);
//clearBuffer = true;
clearActiveWindow(true);
} else {
_useMultiLayers = false;
_DPCR_Reg &= ~(1 << 7);
clearActiveWindow(false);
}
_writeRegister(RA8875_DPCR,_DPCR_Reg);
if (!_useMultiLayers && _color_bpp < 16) setColorBpp(16);//bring color back to 16
/*
if (clearBuffer) {
clearWindow(true);
//for some reason if you switch to multilayer the layer 2 has garbage better clear
//writeTo(L2);//switch to layer 2
//clearMemory(false);//clear memory of layer 2
//clearWindow(false);
//writeTo(L1);//switch to layer 1
}
*/
}
/**************************************************************************/
/*!
*/
/**************************************************************************/
void RA8875::layerEffect(enum RA8875boolean efx)
{
uint8_t reg = 0b00000000;
//reg &= ~(0x07);//clear bit 2,1,0
if (!_useMultiLayers) useLayers(true);//turn on multiple layers if it's off
switch(efx){// bit 2,1,0 of LTPR0
case LAYER1: //only layer 1 visible [000]
//do nothing
break;
case LAYER2: //only layer 2 visible [001]
reg |= (1 << 0);
break;
case TRANSPARENT: //transparent mode [011]
reg |= (1 << 0); reg |= (1 << 1);
break;
case LIGHTEN: //lighten-overlay mode [010]
reg |= (1 << 1);
break;
case OR: //boolean OR mode [100]
reg |= (1 << 2);
break;
case AND: //boolean AND mode [101]
reg |= (1 << 0); reg |= (1 << 2);
break;
case FLOATING: //floating windows [110]
reg |= (1 << 1); reg |= (1 << 2);
break;
default:
//do nothing
break;
}
_writeRegister(RA8875_LTPR0,reg);
}
/**************************************************************************/
/*!
*/
/**************************************************************************/
void RA8875::layerTransparency(uint8_t layer1,uint8_t layer2)
{
if (layer1 > 8) layer1 = 8;
if (layer2 > 8) layer2 = 8;
if (!_useMultiLayers) useLayers(true);//turn on multiple layers if it's off
//if (_useMultiLayers) _writeRegister(RA8875_LTPR1, ((layer2 & 0x0F) << 4) | (layer1 & 0x0F));
//uint8_t res = 0b00000000;//RA8875_LTPR1
//reg &= ~(0x07);//clear bit 2,1,0
_writeRegister(RA8875_LTPR1, ((layer2 & 0xF) << 4) | (layer1 & 0xF));
}
/**************************************************************************/
/*! return the current drawing layer. If layers are OFF, return 255
*/
/**************************************************************************/
uint8_t RA8875::getCurrentLayer(void)
{
if (!_useMultiLayers) return 255;
return _currentLayer;
}
/**************************************************************************/
/*! select pattern
*/
/**************************************************************************/
void RA8875::setPattern(uint8_t num, enum RA8875pattern p)
{
uint8_t maxLoc;
uint8_t temp = 0b00000000;
if (p != P16X16) {
maxLoc = 16;//at 8x8 max 16 locations
} else {
maxLoc = 4;//at 16x16 max 4 locations
temp |= (1 << 7);
}
if (num > (maxLoc - 1)) num = maxLoc - 1;
temp = temp | num;
writeTo(PATTERN);
_writeRegister(RA8875_PTNO,temp);
}
/**************************************************************************/
/*! write pattern
*/
/**************************************************************************/
void RA8875::writePattern(int16_t x,int16_t y,const uint8_t *data,uint8_t size,bool setAW)
{
int16_t i;
int16_t a,b,c,d;
if (size < 8 || size > 16) return;
if (setAW) getActiveWindow(a,b,c,d);
setActiveWindow(x,x+size-1,y,y+size-1);
setXY(x,y);
if (_textMode) _setTextMode(false);//we are in text mode?
writeCommand(RA8875_MRWC);
for (i=0;i<(size*size);i++) {
_writeData(data[i*2]);
_writeData(data[i*2+1]);
_waitBusy(0x80);
}
if (setAW) setActiveWindow(a,b,c,d);//set as it was before
}
/**************************************************************************/
/*! This is the most important function to write on:
LAYERS
CGRAM
PATTERN
CURSOR
Parameter:
d (L1, L2, CGRAM, PATTERN, CURSOR)
When writing on layers 0 or 1, if the layers are not enable it will enable automatically
If the display doesn't support layers, it will automatically switch to 8bit color
Remember that when layers are ON you need to disable manually, once that only Layer 1 will be visible
*/
/**************************************************************************/
void RA8875::writeTo(enum RA8875writes d)
{
uint8_t temp = _readRegister(RA8875_MWCR1);
//bool trigMultilayer = false;
switch(d){
case L1:
temp &= ~((1<<3) | (1<<2));// Clear bits 3 and 2
temp &= ~(1 << 0); //clear bit 0
_currentLayer = 0;
//trigMultilayer = true;
_writeData(temp);
if (!_useMultiLayers) useLayers(true);
break;
case L2:
temp &= ~((1<<3) | (1<<2));// Clear bits 3 and 2
temp |= (1 << 0); //bit set 0
_currentLayer = 1;
//trigMultilayer = true;
_writeData(temp);
if (!_useMultiLayers) useLayers(true);
break;
case CGRAM:
temp &= ~(1 << 3); //clear bit 3
temp |= (1 << 2); //bit set 2
if (bitRead(_FNCR0_Reg,7)){//REG[0x21] bit7 must be 0
_FNCR0_Reg &= ~(1 << 7); //clear bit 7
_writeRegister(RA8875_FNCR0,_FNCR0_Reg);
_writeRegister(RA8875_MWCR1,temp);
} else {
_writeData(temp);
}
break;
case PATTERN:
temp |= (1 << 3); //bit set 3
temp |= (1 << 2); //bit set 2
_writeData(temp);
break;
case CURSOR:
temp |= (1 << 3); //bit set 3
temp &= ~(1 << 2); //clear bit 2
_writeData(temp);
break;
default:
//break;
return;
}
//if (trigMultilayer && !_useMultiLayers) useLayers(true);//turn on multiple layers if it's off
//_writeRegister(RA8875_MWCR1,temp);
}
/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ GEOMETRIC PRIMITIVE STUFF +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/**************************************************************************/
/*!
*/
/**************************************************************************/
/* void RA8875::fillRect(void) {
writeCommand(RA8875_DCR);
_writeData(RA8875_DCR_LINESQUTRI_STOP | RA8875_DCR_DRAWSQUARE);
_writeData(RA8875_DCR_LINESQUTRI_START | RA8875_DCR_FILL | RA8875_DCR_DRAWSQUARE);
}
*/
/**************************************************************************/
/*!
Write a single pixel
Parameters:
x: horizontal pos
y: vertical pos
color: RGB565 color
NOTE:
In 8bit bpp RA8875 needs a 8bit color(332) and NOT a 16bit(565),
the routine deal with this...
*/
/**************************************************************************/
void RA8875::drawPixel(int16_t x, int16_t y, uint16_t color)
{
//setXY(x,y);
if (_textMode) _setTextMode(false);//we are in text mode?
setXY(x,y);
writeCommand(RA8875_MRWC);
if (_color_bpp > 8){
writeData16(color);
} else {//TOTEST:layer bug workaround for 8bit color!
_writeData(_color16To8bpp(color));
}
}
/**************************************************************************/
/*!
Draw a series of pixels
Parameters:
p: an array of 16bit colors (pixels)
count: how many pixels
x: horizontal pos
y: vertical pos
NOTE:
In 8bit bpp RA8875 needs a 8bit color(332) and NOT a 16bit(565),
the routine deal with this...
*/
/**************************************************************************/
void RA8875::drawPixels(uint16_t p[], uint16_t count, int16_t x, int16_t y)
{
//setXY(x,y);
uint16_t temp = 0;
uint16_t i;
if (_textMode) _setTextMode(false);//we are in text mode?
setXY(x,y);
writeCommand(RA8875_MRWC);
_startSend();
//set data
#if (defined(__AVR__) && defined(_FASTSSPORT)) || defined(SPARK)
_spiwrite(RA8875_DATAWRITE);
#else
#if defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) || defined(__MKL26Z64__)
_pspi->transfer(RA8875_DATAWRITE);
#else
SPI.transfer(RA8875_DATAWRITE);
#endif
#endif
//the loop
for (i=0;i= 160) || (TEENSYDUINO > 121))
#if defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) || defined(__MKL26Z64__)
if (_color_bpp > 8){
_pspi->transfer16(temp);
} else {//TOTEST:layer bug workaround for 8bit color!
_pspi->transfer(temp & 0xFF);
}
#else
if (_color_bpp > 8){
SPI.transfer16(temp);
} else {//TOTEST:layer bug workaround for 8bit color!
SPI.transfer(temp & 0xFF);
}
#endif
#else
#if defined(___DUESTUFF) && defined(SPI_DUE_MODE_EXTENDED)
if (_color_bpp > 8){
SPI.transfer(_cs, temp >> 8, SPI_CONTINUE);
SPI.transfer(_cs, temp & 0xFF, SPI_LAST);
} else {//TOTEST:layer bug workaround for 8bit color!
SPI.transfer(_cs, temp & 0xFF, SPI_LAST);
}
#else
#if (defined(__AVR__) && defined(_FASTSSPORT)) || defined(SPARK)
if (_color_bpp > 8){
_spiwrite16(temp);
} else {//TOTEST:layer bug workaround for 8bit color!
_spiwrite(temp >> 8);
}
#else
if (_color_bpp > 8){
SPI.transfer(temp >> 8);
SPI.transfer(temp & 0xFF);
} else {//TOTEST:layer bug workaround for 8bit color!
SPI.transfer(temp & 0xFF);
}
#endif
#endif
#endif
}
_endSend();
}
/**************************************************************************/
/*!
Get a pixel color from screen
Parameters:
x: horizontal pos
y: vertical pos
*/
/**************************************************************************/
uint16_t RA8875::getPixel(int16_t x, int16_t y)
{
uint16_t color;
setXY(x,y);
if (_textMode) _setTextMode(false);//we are in text mode?
writeCommand(RA8875_MRWC);
#if defined(_FASTCPU)
_slowDownSPI(true);
#endif
_startSend();
#if (defined(__AVR__) && defined(_FASTSSPORT)) || defined(SPARK)
_spiwrite(RA8875_DATAREAD);
_spiwrite(0x00);
#else
#if defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) || defined(__MKL26Z64__)
_pspi->transfer(RA8875_DATAREAD);
_pspi->transfer(0x00);//first byte it's dummy
#else
SPI.transfer(RA8875_DATAREAD);
SPI.transfer(0x00);//first byte it's dummy
#endif
#endif
#if !defined(___DUESTUFF) && ((ARDUINO >= 160) || (TEENSYDUINO > 121))
#if defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) || defined(__MKL26Z64__)
color = _pspi->transfer16(0x0);
#else
color = SPI.transfer16(0x0);
#endif
#else
#if defined(___DUESTUFF) && defined(SPI_DUE_MODE_EXTENDED)
color = SPI.transfer(_cs, 0x0, SPI_CONTINUE);
color |= (SPI.transfer(_cs, 0x0, SPI_LAST) << 8);
#else
#if (defined(__AVR__) && defined(_FASTSSPORT)) || defined(SPARK)
color = _spiread();
color |= (_spiread() << 8);
#else
color = SPI.transfer(0x0);
color |= (SPI.transfer(0x0) << 8);
#endif
#endif
#endif
#if defined(_FASTCPU)
_slowDownSPI(false);
#endif
_endSend();
return color;
}
/*
void RA8875::getPixels(uint16_t * p, uint32_t count, int16_t x, int16_t y)
{
uint16_t color;
if (_textMode) _setTextMode(false);//we are in text mode?
setXY(x,y);
writeCommand(RA8875_MRWC);
#if defined(_FASTCPU)
_slowDownSPI(true);
#endif
_startSend();
SPI.transfer(RA8875_DATAREAD);
#if !defined(ENERGIA) && !defined(__SAM3X8E__) && ((ARDUINO >= 160) || (TEENSYDUINO > 121))
SPI.transfer16(0x0);//dummy
#else
SPI.transfer(0x0);//dummy
SPI.transfer(0x0);//dummy
#endif
while (count--) {
#if !defined(__SAM3X8E__) && ((ARDUINO >= 160) || (TEENSYDUINO > 121))
color = SPI.transfer16(0x0);
#else
color = SPI.transfer(0x0);
color |= (SPI.transfer(0x0) << 8);
#endif
*p++ = color;
}
#if defined(_FASTCPU)
_slowDownSPI(false);
#endif
_endSend();
}
*/
/**************************************************************************/
/*!
Basic line draw
Parameters:
x0: horizontal start pos
y0: vertical start
x1: horizontal end pos
y1: vertical end pos
color: RGB565 color
NOTE:
Remember that this write from->to so: drawLine(0,0,2,0,RA8875_RED);
result a 3 pixel long! (0..1..2)
*/
/**************************************************************************/
void RA8875::drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color)
{
//if ((x0 == x1 && y0 == y1) || ((x1 - x0 == 1) && (y1 - y0 == 1))) {//NEW
if ((x0 == x1 && y0 == y1)) {//Thanks MrTOM
drawPixel(x0,y0,color);
return;
}
//if ((x1 - x0 == 1) && (y1 - y0 == 1)) drawPixel(x0,y0,color);
if (_portrait) { swapvals(x0,y0); swapvals(x1,y1);}
if (_textMode) _setTextMode(false);//we are in text mode?
#if defined(USE_RA8875_SEPARATE_TEXT_COLOR)
_TXTrecoverColor = true;
#endif
if (color != _foreColor) setForegroundColor(color);//0.69b30 avoid 3 useless SPI calls
_line_addressing(x0,y0,x1,y1);
_writeRegister(RA8875_DCR,0x80);
_waitPoll(RA8875_DCR, RA8875_DCR_LINESQUTRI_STATUS, _RA8875_WAITPOLL_TIMEOUT_DCR_LINESQUTRI_STATUS);
}
/**************************************************************************/
/*!
Basic line by using Angle as parameter
Parameters:
x: horizontal start pos
y: vertical start
angle: the angle of the line
length: lenght of the line
color: RGB565 color
*/
/**************************************************************************/
void RA8875::drawLineAngle(int16_t x, int16_t y, int16_t angle, uint16_t length, uint16_t color,int offset)
{
if (length < 2) {
drawPixel(x,y,color);
} else {
length--;//n
drawLine(
x,
y,
//x + (length * _cosDeg_helper(angle + offset)),//_angle_offset
x + round((length) * _cosDeg_helper(angle + offset)),//Thanks MrTom
//y + (length * _sinDeg_helper(angle + offset)),
y + round((length) * _sinDeg_helper(angle + offset)),//Thanks MrTom
color);
}
}
/**************************************************************************/
/*!
Basic line by using Angle as parameter
Parameters:
x: horizontal start pos
y: vertical start
angle: the angle of the line
start: where line start
length: lenght of the line
color: RGB565 color
*/
/**************************************************************************/
void RA8875::drawLineAngle(int16_t x, int16_t y, int16_t angle, uint16_t start, uint16_t length, uint16_t color,int offset)
{
if (length < 2) {
drawPixel(
x + round(start * _cosDeg_helper(angle + offset)),
y + round(start * _sinDeg_helper(angle + offset)),
color);
} else {
length--;//n
drawLine(
//x + start * _cosDeg_helper(angle + offset),//_angle_offset
x + round(start * _cosDeg_helper(angle + offset)),//Thanks MrTom
//y + start * _sinDeg_helper(angle + offset),
y + round(start * _sinDeg_helper(angle + offset)),//Thanks MrTom
//x + (start + length) * _cosDeg_helper(angle + offset),
x + round((start + length) * _cosDeg_helper(angle + offset)),//Thanks MrTom
//y + (start + length) * _sinDeg_helper(angle + offset),
y + round((start + length) * _sinDeg_helper(angle + offset)), //Thanks MrTom
color);
}
}
void RA8875::roundGaugeTicker(uint16_t x, uint16_t y, uint16_t r, int from, int to, float dev,uint16_t color)
{
float dsec;
int i;
for (i = from; i <= to; i += 30) {
dsec = i * (PI / 180);
drawLine(
x + (cos(dsec) * (r / dev)) + 1,
y + (sin(dsec) * (r / dev)) + 1,
x + (cos(dsec) * r) + 1,
y + (sin(dsec) * r) + 1,
color);
}
}
/**************************************************************************/
/*!
for compatibility with popular Adafruit_GFX
draws a single vertical line
Parameters:
x: horizontal start
y: vertical start
h: height
color: RGB565 color
*/
/**************************************************************************/
void RA8875::drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color)
{
if (h < 1) h = 1;
h < 2 ? drawPixel(x,y,color) : drawLine(x, y, x, (y+h)-1, color);
}
/**************************************************************************/
/*!
for compatibility with popular Adafruit_GFX
draws a single orizontal line
Parameters:
x: horizontal start
y: vertical start
w: width
color: RGB565 color
*/
/**************************************************************************/
void RA8875::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color)
{
if (w < 1) w = 1;
w < 2 ? drawPixel(x,y,color) : drawLine(x, y, (w+x)-1, y, color);
}
/**************************************************************************/
/*!
draws a rectangle
Parameters:
x: horizontal start
y: vertical start
w: width
h: height
color: RGB565 color
*/
/**************************************************************************/
void RA8875::drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)
{
//RA8875 it's not out-of-range tolerant so this is a workaround
if (w < 1 || h < 1) return;//it cannot be!
if (w < 2 && h < 2){ //render as pixel
drawPixel(x,y,color);
} else { //render as rect
_rect_helper(x,y,(w+x)-1,(h+y)-1,color,false);//thanks the experimentalist
}
}
/**************************************************************************/
/*!
draws a FILLED rectangle
Parameters:
x: horizontal start
y: vertical start
w: width
h: height
color: RGB565 color
*/
/**************************************************************************/
void RA8875::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)
{
//RA8875 it's not out-of-range tolerant so this is a workaround
if (w < 1 || h < 1) return;//it cannot be!
if (w < 2 && h < 2){ //render as pixel
drawPixel(x,y,color);
} else { //render as rect
//Serial.printf(" fillRect: %d %d %d %d %x\n", x, y, w, h, color);
_rect_helper(x,y,(x+w)-1,(y+h)-1,color,true);//thanks the experimentalist
}
}
/**************************************************************************/
/*!
write a filled rectangle wither user colors array
Parameters:
x: horizontal start
y: vertical start
w: width
h: height
pcolors: Array of RGB565 color of size w*h
*/
/**************************************************************************/
void RA8875::writeRect(int16_t x, int16_t y, int16_t w, int16_t h, const uint16_t *pcolors)
{
uint16_t start_x = (x != CENTER) ? x : (_width - w) / 2;
uint16_t start_y = (y != CENTER) ? y : (_height - h) / 2;
static uint16_t rotated_row[800]; // max size.
bool portrait_mode = isPortrait();
if (portrait_mode) {
pcolors += (w - 1);
for (uint16_t x = start_x + w - 1; x >= start_x; x--) {
const uint16_t *pimage = pcolors;
for (uint16_t i = 0; i < h; i++) {
rotated_row[i] = *pimage;
pimage += w;
}
//Serial.printf("DP %x, %d, %d %d\n", rotated_row, h, start_x, y);
drawPixels(rotated_row, h, x, start_y);
pcolors--;
}
} else {
// now lets draw out each of the lines of the image...
for (uint16_t y = start_y; y < (start_y + h); y++) {
// tft.setY(y); // Not needed drawPixels calls setXY which will set y...
drawPixels((uint16_t*)pcolors, w, start_x, y);
pcolors += w;
}
}
}
/**************************************************************************/
/*!
calculate a grandient color
return a spectrum starting at blue to red (0...127)
*/
/**************************************************************************/
uint16_t RA8875::grandient(uint8_t val)
{
uint8_t r = 0;
uint8_t g = 0;
uint8_t b = 0;
uint8_t q = val / 32;
switch(q){
case 0:
r = 0; g = 2 * (val % 32); b = 31;
break;
case 1:
r = 0; g = 63; b = 31 - (val % 32);
break;
case 2:
r = val % 32; g = 63; b = 0;
break;
case 3:
r = 31; g = 63 - 2 * (val % 32); b = 0;
break;
}
return (r << 11) + (g << 5) + b;
}
/**************************************************************************/
/*!
interpolate 2 16bit colors
return a 16bit mixed color between the two
Parameters:
color1:
color2:
pos:0...div (mix percentage) (0:color1, div:color2)
div:divisions between color1 and color 2
*/
/**************************************************************************/
uint16_t RA8875::colorInterpolation(uint16_t color1,uint16_t color2,uint16_t pos,uint16_t div)
{
if (pos == 0) return color1;
if (pos >= div) return color2;
uint8_t r1,g1,b1;
Color565ToRGB(color1,r1,g1,b1);//split in r,g,b
uint8_t r2,g2,b2;
Color565ToRGB(color2,r2,g2,b2);//split in r,g,b
return colorInterpolation(r1,g1,b1,r2,g2,b2,pos,div);
}
/**************************************************************************/
/*!
interpolate 2 r,g,b colors
return a 16bit mixed color between the two
Parameters:
r1.
g1:
b1:
r2:
g2:
b2:
pos:0...div (mix percentage) (0:color1, div:color2)
div:divisions between color1 and color 2
*/
/**************************************************************************/
uint16_t RA8875::colorInterpolation(uint8_t r1,uint8_t g1,uint8_t b1,uint8_t r2,uint8_t g2,uint8_t b2,uint16_t pos,uint16_t div)
{
if (pos == 0) return Color565(r1,g1,b1);
if (pos >= div) return Color565(r2,g2,b2);
float pos2 = (float)pos/div;
return Color565(
(uint8_t)(((1.0 - pos2) * r1) + (pos2 * r2)),
(uint8_t)((1.0 - pos2) * g1 + (pos2 * g2)),
(uint8_t)(((1.0 - pos2) * b1) + (pos2 * b2))
);
}
/**************************************************************************/
/*!
draws a dots filled area
Parameters:
x: horizontal origin
y: vertical origin
w: width
h: height
spacing: space between dots in pixels (min 2pix)
color: RGB565 color
*/
/**************************************************************************/
void RA8875::drawMesh(int16_t x, int16_t y, int16_t w, int16_t h, uint8_t spacing, uint16_t color)
{
if (spacing < 2) spacing = 2;
if (((x + w) - 1) >= _width) w = _width - x;
if (((y + h) - 1) >= _height) h = _height - y;
int16_t n, m;
if (w < x) {n = w; w = x; x = n;}
if (h < y) {n = h; h = y; y = n;}
for (m = y; m <= h; m += spacing) {
for (n = x; n <= w; n += spacing) {
drawPixel(n, m, color);
}
}
}
/**************************************************************************/
/*!
Fill the ActiveWindow by using a specified RGB565 color
Parameters:
color: RGB565 color (default=BLACK)
*/
/**************************************************************************/
void RA8875::fillWindow(uint16_t color)
{
_line_addressing(0,0,RA8875_WIDTH-1, RA8875_HEIGHT-1);
setForegroundColor(color);
writeCommand(RA8875_DCR);
_writeData(0xB0);
_waitPoll(RA8875_DCR, RA8875_DCR_LINESQUTRI_STATUS, _RA8875_WAITPOLL_TIMEOUT_DCR_LINESQUTRI_STATUS);
#if defined(USE_RA8875_SEPARATE_TEXT_COLOR)
_TXTrecoverColor = true;
#endif
}
/**************************************************************************/
/*!
clearScreen it's different from fillWindow because it doesn't depends
from the active window settings so it will clear all the screen.
It should be used only when needed since it's slower than fillWindow.
parameter:
color: 16bit color (default=BLACK)
*/
/**************************************************************************/
void RA8875::clearScreen(uint16_t color)//0.69b24
{
setActiveWindow();
fillWindow(color);
}
/**************************************************************************/
/*!
Draw circle
Parameters:
x0: The 0-based x location of the center of the circle
y0: The 0-based y location of the center of the circle
r: radius
color: RGB565 color
*/
/**************************************************************************/
void RA8875::drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color)
{
_center_helper(x0,y0);
if (r < 1) return;
if (r < 2) {
drawPixel(x0,y0,color);
return;
}
_circle_helper(x0, y0, r, color, false);
}
/**************************************************************************/
/*!
Draw filled circle
Parameters:
x0: The 0-based x location of the center of the circle
y0: The 0-based y location of the center of the circle
r: radius
color: RGB565 color
*/
/**************************************************************************/
/*
void RA8875::fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color)
{
_center_helper(x0,y0);
if (r <= 0) return;
_circle_helper(x0, y0, r, color, true);
}
*/
void RA8875::fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color)
{
_center_helper(x0,y0);
if (r < 1) return;
if (r == 1) {
drawPixel(x0,y0,color);
return;
}
_circle_helper(x0, y0, r, color, true);
}
/**************************************************************************/
/*!
Draw a quadrilater by connecting 4 points
Parameters:
x0:
y0:
x1:
y1:
x2:
y2:
x3:
y3:
color: RGB565 color
*/
/**************************************************************************/
void RA8875::drawQuad(int16_t x0, int16_t y0,int16_t x1, int16_t y1,int16_t x2, int16_t y2,int16_t x3, int16_t y3, uint16_t color)
{
drawLine(x0, y0, x1, y1, color);//low 1
drawLine(x1, y1, x2, y2, color);//high 1
drawLine(x2, y2, x3, y3, color);//high 2
drawLine(x3, y3, x0, y0, color);//low 2
}
/**************************************************************************/
/*!
Draw a filled quadrilater by connecting 4 points
Parameters:
x0:
y0:
x1:
y1:
x2:
y2:
x3:
y3:
color: RGB565 color
triangled: if true a full quad will be generated, false generate a low res quad (faster)
*NOTE: a bug in _triangle_helper create some problem, still fixing....
*/
/**************************************************************************/
void RA8875::fillQuad(int16_t x0, int16_t y0,int16_t x1, int16_t y1,int16_t x2, int16_t y2, int16_t x3, int16_t y3, uint16_t color, bool triangled)
{
_triangle_helper(x0, y0, x1, y1, x2, y2, color,true);
if (triangled) _triangle_helper(x2, y2, x3, y3, x0, y0, color,true);
_triangle_helper(x1, y1, x2, y2, x3, y3, color,true);
}
/**************************************************************************/
/*!
Draw a polygon from a center
Parameters:
cx: x center of the polygon
cy: y center of the polygon
sides: how many sides (min 3)
diameter: diameter of the polygon
rot: angle rotation of the polygon
color: RGB565 color
*/
/**************************************************************************/
void RA8875::drawPolygon(int16_t cx, int16_t cy, uint8_t sides, int16_t diameter, float rot, uint16_t color)
{
_center_helper(cx,cy);
sides = (sides > 2? sides : 3);
float dtr = (PI / 180.0) + PI;
float rads = 360.0 / sides;//points spacd equally
uint8_t i;
for (i = 0; i < sides; i++) {
drawLine(
cx + (sin((i*rads + rot) * dtr) * diameter),
cy + (cos((i*rads + rot) * dtr) * diameter),
cx + (sin(((i+1)*rads + rot) * dtr) * diameter),
cy + (cos(((i+1)*rads + rot) * dtr) * diameter),
color);
}
}
/**************************************************************************/
/*!
ringMeter
(adapted from Alan Senior (thanks man!))
it create a ring meter with a lot of personalizations,
it return the width of the gauge so you can use this value
for positioning other gauges near the one just created easily
Parameters:
val: your value
minV: the minimum value possible
maxV: the max value possible
x: the position on x axis
y: the position on y axis
r: the radius of the gauge (minimum 50)
units: a text that shows the units, if "none" all text will be avoided
scheme:0...7 or 16 bit color (not BLACK or WHITE)
0:red
1:green
2:blue
3:blue->red
4:green->red
5:red->green
6:red->green->blue
7:cyan->green->red
8:black->white linear interpolation
9:violet->yellow linear interpolation
or
RGB565 color (not BLACK or WHITE)
backSegColor: the color of the segments not active (default BLACK)
angle: 90 -> 180 (the shape of the meter, 90:halfway, 180:full round, 150:default)
inc: 5...20 (5:solid, 20:sparse divisions, default:10)
*/
/**************************************************************************/
void RA8875::ringMeter(int val, int minV, int maxV, int16_t x, int16_t y, uint16_t r, const char* units, uint16_t colorScheme,uint16_t backSegColor,int16_t angle,uint8_t inc)
{
if (inc < 5) inc = 5;
if (inc > 20) inc = 20;
if (r < 50) r = 50;
if (angle < 90) angle = 90;
if (angle > 180) angle = 180;
int curAngle = map(val, minV, maxV, -angle, angle);
uint16_t colour;
x += r;
y += r; // Calculate coords of centre of ring
uint16_t w = r / 4; // Width of outer ring is 1/4 of radius
const uint8_t seg = 5; // Segments are 5 degrees wide = 60 segments for 300 degrees
// Draw colour blocks every inc degrees
for (int16_t i = -angle; i < angle; i += inc) {
colour = RA8875_BLACK;
switch (colorScheme) {
case 0:
colour = RA8875_RED;
break; // Fixed colour
case 1:
colour = RA8875_GREEN;
break; // Fixed colour
case 2:
colour = RA8875_BLUE;
break; // Fixed colour
case 3:
colour = grandient(map(i, -angle, angle, 0, 127));
break; // Full spectrum blue to red
case 4:
colour = grandient(map(i, -angle, angle, 63, 127));
break; // Green to red (high temperature etc)
case 5:
colour = grandient(map(i, -angle, angle, 127, 63));
break; // Red to green (low battery etc)
case 6:
colour = grandient(map(i, -angle, angle, 127, 0));
break; // Red to blue (air cond reverse)
case 7:
colour = grandient(map(i, -angle, angle, 35, 127));
break; // cyan to red
case 8:
colour = colorInterpolation(0,0,0,255,255,255,map(i,-angle,angle,0,w),w);
break; // black to white
case 9:
colour = colorInterpolation(0x80,0,0xC0,0xFF,0xFF,0,map(i,-angle,angle,0,w),w);
break; // violet to yellow
default:
if (colorScheme > 9){
colour = colorScheme;
} else {
colour = RA8875_BLUE;
}
break; // Fixed colour
}
// Calculate pair of coordinates for segment start
float xStart = cos((i - 90) * 0.0174532925);
float yStart = sin((i - 90) * 0.0174532925);
uint16_t x0 = xStart * (r - w) + x;
uint16_t y0 = yStart * (r - w) + y;
uint16_t x1 = xStart * r + x;
uint16_t y1 = yStart * r + y;
// Calculate pair of coordinates for segment end
float xEnd = cos((i + seg - 90) * 0.0174532925);
float yEnd = sin((i + seg - 90) * 0.0174532925);
int16_t x2 = xEnd * (r - w) + x;
int16_t y2 = yEnd * (r - w) + y;
int16_t x3 = xEnd * r + x;
int16_t y3 = yEnd * r + y;
if (i < curAngle) { // Fill in coloured segments with 2 triangles
fillQuad(x0, y0, x1, y1, x2, y2, x3, y3, colour, false);
} else {// Fill in blank segments
fillQuad(x0, y0, x1, y1, x2, y2, x3, y3, backSegColor, false);
}
}
// text
if (strcmp(units, "none") != 0){
//erase internal background
if (angle > 90) {
fillCircle(x, y, r - w, _backColor);
} else {
fillCurve(x, y + getFontHeight() / 2, r - w, r - w, 1, _backColor);
fillCurve(x, y + getFontHeight() / 2, r - w, r - w, 2, _backColor);
}
//prepare for write text
if (r > 84) {
setFontScale(1);
} else {
setFontScale(0);
}
if (_portrait){
setCursor(y, x - 15, true);
} else {
setCursor(x - 15, y, true);
}
print(val);
print(" ");
print(units);
}
// Calculate and return right hand side x coordinate
//return x + r;
}
/**************************************************************************/
/*!
Draw Triangle
Parameters:
x0: The 0-based x location of the point 0 of the triangle bottom LEFT
y0: The 0-based y location of the point 0 of the triangle bottom LEFT
x1: The 0-based x location of the point 1 of the triangle middle TOP
y1: The 0-based y location of the point 1 of the triangle middle TOP
x2: The 0-based x location of the point 2 of the triangle bottom RIGHT
y2: The 0-based y location of the point 2 of the triangle bottom RIGHT
color: RGB565 color
*/
/**************************************************************************/
void RA8875::drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color)
{
_triangle_helper(x0, y0, x1, y1, x2, y2, color, false);
}
/**************************************************************************/
/*!
Draw filled Triangle
Parameters:
x0: The 0-based x location of the point 0 of the triangle
y0: The 0-based y location of the point 0 of the triangle
x1: The 0-based x location of the point 1 of the triangle
y1: The 0-based y location of the point 1 of the triangle
x2: The 0-based x location of the point 2 of the triangle
y2: The 0-based y location of the point 2 of the triangle
color: RGB565 color
*/
/**************************************************************************/
void RA8875::fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color)
{
_triangle_helper(x0, y0, x1, y1, x2, y2, color, true);
}
/**************************************************************************/
/*!
Draw an ellipse
Parameters:
xCenter: x location of the center of the ellipse
yCenter: y location of the center of the ellipse
longAxis: Size in pixels of the long axis
shortAxis: Size in pixels of the short axis
color: RGB565 color
*/
/**************************************************************************/
void RA8875::drawEllipse(int16_t xCenter, int16_t yCenter, int16_t longAxis, int16_t shortAxis, uint16_t color)
{
_ellipseCurve_helper(xCenter, yCenter, longAxis, shortAxis, 255, color, false);
}
/**************************************************************************/
/*!
Draw a filled ellipse
Parameters:
xCenter: x location of the center of the ellipse
yCenter: y location of the center of the ellipse
longAxis: Size in pixels of the long axis
shortAxis: Size in pixels of the short axis
color: RGB565 color
*/
/**************************************************************************/
void RA8875::fillEllipse(int16_t xCenter, int16_t yCenter, int16_t longAxis, int16_t shortAxis, uint16_t color)
{
_ellipseCurve_helper(xCenter, yCenter, longAxis, shortAxis, 255, color, true);
}
/**************************************************************************/
/*!
Draw a curve
Parameters:
xCenter:] x location of the ellipse center
yCenter: y location of the ellipse center
longAxis: Size in pixels of the long axis
shortAxis: Size in pixels of the short axis
curvePart: Curve to draw in clock-wise dir: 0[180-270°],1[270-0°],2[0-90°],3[90-180°]
color: RGB565 color
*/
/**************************************************************************/
void RA8875::drawCurve(int16_t xCenter, int16_t yCenter, int16_t longAxis, int16_t shortAxis, uint8_t curvePart, uint16_t color)
{
curvePart = curvePart % 4; //limit to the range 0-3
if (_portrait) {//fix a problem with rotation
if (curvePart == 0) {
curvePart = 2;
} else if (curvePart == 2) {
curvePart = 0;
}
}
_ellipseCurve_helper(xCenter, yCenter, longAxis, shortAxis, curvePart, color, false);
}
/**************************************************************************/
/*!
Draw a filled curve
Parameters:
xCenter:] x location of the ellipse center
yCenter: y location of the ellipse center
longAxis: Size in pixels of the long axis
shortAxis: Size in pixels of the short axis
curvePart: Curve to draw in clock-wise dir: 0[180-270°],1[270-0°],2[0-90°],3[90-180°]
color: RGB565 color
*/
/**************************************************************************/
void RA8875::fillCurve(int16_t xCenter, int16_t yCenter, int16_t longAxis, int16_t shortAxis, uint8_t curvePart, uint16_t color)
{
curvePart = curvePart % 4; //limit to the range 0-3
if (_portrait) {//fix a problem with rotation
if (curvePart == 0) {
curvePart = 2;
} else if (curvePart == 2) {
curvePart = 0;
}
}
_ellipseCurve_helper(xCenter, yCenter, longAxis, shortAxis, curvePart, color, true);
}
/**************************************************************************/
/*!
Draw a rounded rectangle
Parameters:
x: x location of the rectangle
y: y location of the rectangle
w: the width in pix
h: the height in pix
r: the radius of the rounded corner
color: RGB565 color
_roundRect_helper it's not tolerant to improper values
so there's some value check here
*/
/**************************************************************************/
void RA8875::drawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color)
{
if (r == 0) drawRect(x,y,w,h,color);
if (w < 1 || h < 1) return;//it cannot be!
if (w < 2 && h < 2){ //render as pixel
drawPixel(x,y,color);
} else { //render as rect
if (w < h && (r * 2) >= w) r = (w / 2) - 1;
if (w > h && (r * 2) >= h) r = (h / 2) - 1;
if (r == w || r == h) drawRect(x,y,w,h,color);
_roundRect_helper(x, y, (x + w) - 1, (y + h) - 1, r, color, false);
}
}
/**************************************************************************/
/*!
Draw a filled rounded rectangle
Parameters:
x: x location of the rectangle
y: y location of the rectangle
w: the width in pix
h: the height in pix
r: the radius of the rounded corner
color: RGB565 color
*/
/**************************************************************************/
void RA8875::fillRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color)
{
if (r == 0) fillRect(x,y,w,h,color);
if (w < 1 || h < 1) return;//it cannot be!
if (w < 2 && h < 2){ //render as pixel
drawPixel(x,y,color);
} else { //render as rect
if (w < h && (r * 2) >= w) r = (w / 2) - 1;
if (w > h && (r *2) >= h) r = (h / 2) - 1;
if (r == w || r == h) drawRect(x,y,w,h,color);
_roundRect_helper(x, y, (x + w) - 1, (y + h) - 1, r, color, true);
}
}
/**************************************************************************/
/*!
check area of a triangle
[private]
Thanks MrTom
*/
/**************************************************************************/
float RA8875::_check_area(int16_t Ax, int16_t Ay, int16_t Bx, int16_t By, int16_t Cx, int16_t Cy) {
float area = abs(Ax * (By - Cy) + Bx * (Cy - Ay) + Cx * (Ay - By)); // Calc area
float mag1 = sqrt((Bx - Ax) * (Bx - Ax) + (By - Ay) * (By - Ay)); // Calc side lengths
float mag2 = sqrt((Cx - Ax) * (Cx - Ax) + (Cy - Ay) * (Cy - Ay));
float mag3 = sqrt((Cx - Bx) * (Cx - Bx) + (Cy - By) * (Cy - By));
float magmax = (mag1>mag2?mag1:mag2)>mag3?(mag1>mag2?mag1:mag2):mag3; // Find largest length
return area/magmax; // Return area
}
/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ GEOMETRIC PRIMITIVE HELPERS STUFF +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/**************************************************************************/
/*!
helper function for circles
[private]
*/
/**************************************************************************/
void RA8875::_circle_helper(int16_t x0, int16_t y0, int16_t r, uint16_t color, bool filled)//0.69b32 fixed an undocumented hardware limit
{
if (_portrait) swapvals(x0,y0);//0.69b21
if (r < 1) r = 1;
if (r < 2) {//NEW
drawPixel(x0,y0,color);
return;
}
if (r > RA8875_HEIGHT / 2) r = (RA8875_HEIGHT / 2) - 1;//this is the (undocumented) hardware limit of RA8875
if (_textMode) _setTextMode(false);//we are in text mode?
#if defined(USE_RA8875_SEPARATE_TEXT_COLOR)
_TXTrecoverColor = true;
#endif
if (color != _foreColor) setForegroundColor(color);//0.69b30 avoid several SPI calls
_writeRegister(RA8875_DCHR0, x0 & 0xFF);
_writeRegister(RA8875_DCHR0 + 1,x0 >> 8);
_writeRegister(RA8875_DCVR0, y0 & 0xFF);
_writeRegister(RA8875_DCVR0 + 1,y0 >> 8);
_writeRegister(RA8875_DCRR,r);
writeCommand(RA8875_DCR);
#if defined(_FASTCPU)
_slowDownSPI(true);
#endif
filled == true ? _writeData(RA8875_DCR_CIRCLE_START | RA8875_DCR_FILL) : _writeData(RA8875_DCR_CIRCLE_START | RA8875_DCR_NOFILL);
_waitPoll(RA8875_DCR, RA8875_DCR_CIRCLE_STATUS, _RA8875_WAITPOLL_TIMEOUT_DCR_CIRCLE_STATUS);//ZzZzz
#if defined(_FASTCPU)
_slowDownSPI(false);
#endif
}
/**************************************************************************/
/*!
helper function for rects (filled or not)
[private]
*/
/**************************************************************************/
/*
void RA8875::_rect_helper(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color, bool filled)
{
if (w < 0 || h < 0) return;//why draw invisible rects?(MrTOM temp fix)
if (w >= _width) return;
if (h >= _height) return;
if (_portrait) {swapvals(x,y); swapvals(w,h);}
_checkLimits_helper(x,y);
if (_textMode) _setTextMode(false);//we are in text mode?
#if defined(USE_RA8875_SEPARATE_TEXT_COLOR)
_TXTrecoverColor = true;
#endif
if (color != _foreColor) setForegroundColor(color);
_line_addressing(x,y,w,h);
writeCommand(RA8875_DCR);
filled == true ? _writeData(0xB0) : _writeData(0x90);
_waitPoll(RA8875_DCR, RA8875_DCR_LINESQUTRI_STATUS);
}
*/
void RA8875::_rect_helper(int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color, bool filled)
{
if (_portrait) {swapvals(x1,y1); swapvals(x2,y2);}
if ((x1 < 0 && x2 < 0) || (x1 >= RA8875_WIDTH && x2 >= RA8875_WIDTH) ||
(y1 < 0 && y2 < 0) || (y1 >= RA8875_HEIGHT && y2 >= RA8875_HEIGHT))
return; // All points are out of bounds, don't draw anything
_checkLimits_helper(x1,y1); // Truncate rectangle that is off screen, still draw remaining rectangle
_checkLimits_helper(x2,y2);
if (_textMode) _setTextMode(false); //we are in text mode?
#if defined(USE_RA8875_SEPARATE_TEXT_COLOR)
_TXTrecoverColor = true;
#endif
if (color != _foreColor) setForegroundColor(color);
if (x1==x2 && y1==y2) // Width & height can still be 1 pixel, so render as a pixel
drawPixel(x1,y1,color);
else {
_line_addressing(x1,y1,x2,y2);
writeCommand(RA8875_DCR);
filled == true ? _writeData(0xB0) : _writeData(0x90);
_waitPoll(RA8875_DCR, RA8875_DCR_LINESQUTRI_STATUS, _RA8875_WAITPOLL_TIMEOUT_DCR_LINESQUTRI_STATUS);
}
}
/**************************************************************************/
/*!
helper function for triangles
[private]
*/
/**************************************************************************/
void RA8875::_triangle_helper(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color, bool filled)
{
if (x0 >= _width || x1 >= _width || x2 >= _width) return;
if (y0 >= _height || y1 >= _height || y2 >= _height) return;
if (_portrait) {swapvals(x0,y0); swapvals(x1,y1); swapvals(x2,y2);}
/*
if (x0 == x1 && y0 == y1){
drawLine(x0, y0, x2, y2,color);
return;
} else if (x0 == x2 && y0 == y2){
drawLine(x0, y0, x1, y1,color);
return;
} else if (x0 == x1 && y0 == y1 && x0 == x2 && y0 == y2) {//new
drawPixel(x0, y0, color);
return;
}
*/
/*
if (y0 > y1) {swapvals(y0, y1); swapvals(x0, x1);} // Sort points from Y < to >
if (y1 > y2) {swapvals(y2, y1); swapvals(x2, x1);}
if (y0 > y1) {swapvals(y0, y1); swapvals(x0, x1);}
*/
/*
if (y0 == y2) { // Handle awkward all-on-same-line case as its own thing
int16_t a, b;
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;
}
*/
// Avoid drawing lines here due to hardware bug in certain circumstances when a
// specific shape triangle is drawn after a line. This bug can still happen, but
// at least the user has control over fixing it.
// Not drawing a line here is slower, but drawing a non-filled "triangle" is
// slightly faster than a filled "triangle".
//
// bug example: tft.drawLine(799,479, 750,50, RA8875_BLUE)
// tft.fillTriangle(480,152, 456,212, 215,410, RA8875_GREEN)
// MrTom
//
if (x0 == x1 && y0 == y1 && x0 == x2 && y0 == y2) { // All points are same
drawPixel(x0,y0, color);
return;
} else if ((x0 == x1 && y0 == y1) || (x0 == x2 && y0 == y2) || (x1 == x2 && y1 == y2)){
filled = false; // Two points are same
} else if (x0 == x1 && x0 == x2){
filled = false; // Vertical line
} else if (y0 == y1 && y0 == y2){
filled = false; // Horizontal line
}
if (filled){
if (_check_area(x0,y0, x1,y1, x2,y2) < 0.9) {
filled = false; // Draw non-filled triangle to avoid filled triangle bug when two vertices are close together.
}
}
if (_textMode) _setTextMode(false);//we are in text mode?
#if defined(USE_RA8875_SEPARATE_TEXT_COLOR)
_TXTrecoverColor = true;
#endif
if (color != _foreColor) setForegroundColor(color);//0.69b30 avoid several SPI calls
//_checkLimits_helper(x0,y0);
//_checkLimits_helper(x1,y1);
_line_addressing(x0,y0,x1,y1);
//p2
_writeRegister(RA8875_DTPH0, x2 & 0xFF);
_writeRegister(RA8875_DTPH0 + 1,x2 >> 8);
_writeRegister(RA8875_DTPV0, y2 & 0xFF);
_writeRegister(RA8875_DTPV0 + 1,y2 >> 8);
writeCommand(RA8875_DCR);
filled == true ? _writeData(0xA1) : _writeData(0x81);
_waitPoll(RA8875_DCR, RA8875_DCR_LINESQUTRI_STATUS, _RA8875_WAITPOLL_TIMEOUT_DCR_LINESQUTRI_STATUS);
}
/**************************************************************************/
/*!
helper function for ellipse and curve
[private]
*/
/**************************************************************************/
void RA8875::_ellipseCurve_helper(int16_t xCenter, int16_t yCenter, int16_t longAxis, int16_t shortAxis, uint8_t curvePart,uint16_t color, bool filled)
{
_center_helper(xCenter,yCenter);//use CENTER?
if (_portrait) {
swapvals(xCenter,yCenter);
swapvals(longAxis,shortAxis);
if (longAxis > _height/2) longAxis = (_height / 2) - 1;
if (shortAxis > _width/2) shortAxis = (_width / 2) - 1;
} else {
if (longAxis > _width/2) longAxis = (_width / 2) - 1;
if (shortAxis > _height/2) shortAxis = (_height / 2) - 1;
}
if (longAxis == 1 && shortAxis == 1) {
drawPixel(xCenter,yCenter,color);
return;
}
_checkLimits_helper(xCenter,yCenter);
if (_textMode) _setTextMode(false);//we are in text mode?
#if defined(USE_RA8875_SEPARATE_TEXT_COLOR)
_TXTrecoverColor = true;
#endif
if (color != _foreColor) setForegroundColor(color);
_curve_addressing(xCenter,yCenter,longAxis,shortAxis);
writeCommand(RA8875_ELLIPSE);
if (curvePart != 255){
curvePart = curvePart % 4; //limit to the range 0-3
filled == true ? _writeData(0xD0 | (curvePart & 0x03)) : _writeData(0x90 | (curvePart & 0x03));
} else {
filled == true ? _writeData(0xC0) : _writeData(0x80);
}
_waitPoll(RA8875_ELLIPSE, RA8875_ELLIPSE_STATUS, _RA8875_WAITPOLL_TIMEOUT_ELLIPSE_STATUS);
}
/**************************************************************************/
/*!
helper function for rounded Rects
PARAMETERS
x:
y:
w:
h:
r:
color:
filled:
[private]
*/
/**************************************************************************/
void RA8875::_roundRect_helper(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color, bool filled)
{
if (_portrait) {swapvals(x,y); swapvals(w,h);}
if (_textMode) _setTextMode(false);//we are in text mode?
#if defined(USE_RA8875_SEPARATE_TEXT_COLOR)
_TXTrecoverColor = true;
#endif
if (color != _foreColor) setForegroundColor(color);//0.69b30 avoid several SPI calls
_line_addressing(x,y,w,h);
_writeRegister(RA8875_ELL_A0, r & 0xFF);
_writeRegister(RA8875_ELL_A0 + 1,r >> 8);
_writeRegister(RA8875_ELL_B0, r & 0xFF);
_writeRegister(RA8875_ELL_B0 + 1,r >> 8);
writeCommand(RA8875_ELLIPSE);
filled == true ? _writeData(0xE0) : _writeData(0xA0);
_waitPoll(RA8875_ELLIPSE, RA8875_DCR_LINESQUTRI_STATUS, _RA8875_WAITPOLL_TIMEOUT_DCR_LINESQUTRI_STATUS);
}
/**************************************************************************/
/*!
helper function for draw arcs in degrees
DrawArc function thanks to Jnmattern and his Arc_2.0 (https://github.com/Jnmattern)
Adapted for DUE by Marek Buriak https://github.com/marekburiak/ILI9341_Due
Re-Adapted for this library by sumotoy
PARAMETERS
cx: center x
cy: center y
radius: the radius of the arc
thickness:
start: where arc start in degrees
end: where arc end in degrees
color:
[private]
*/
/**************************************************************************/
void RA8875::_drawArc_helper(uint16_t cx, uint16_t cy, uint16_t radius, uint16_t thickness, float start, float end, uint16_t color) {
//_center_helper(cx,cy);//use CENTER?
int16_t xmin = 65535, xmax = -32767, ymin = 32767, ymax = -32767;
float cosStart, sinStart, cosEnd, sinEnd;
float r, t;
float startAngle, endAngle;
startAngle = (start / _arcAngle_max) * 360; // 252
endAngle = (end / _arcAngle_max) * 360; // 807
while (startAngle < 0) startAngle += 360;
while (endAngle < 0) endAngle += 360;
while (startAngle > 360) startAngle -= 360;
while (endAngle > 360) endAngle -= 360;
if (startAngle > endAngle) {
_drawArc_helper(cx, cy, radius, thickness, ((startAngle) / (float)360) * _arcAngle_max, _arcAngle_max, color);
_drawArc_helper(cx, cy, radius, thickness, 0, ((endAngle) / (float)360) * _arcAngle_max, color);
} else {
//if (_textMode) _setTextMode(false);//we are in text mode?
// Calculate bounding box for the arc to be drawn
cosStart = _cosDeg_helper(startAngle);
sinStart = _sinDeg_helper(startAngle);
cosEnd = _cosDeg_helper(endAngle);
sinEnd = _sinDeg_helper(endAngle);
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;
// 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;
if (endAngle == 360) eslope = -1000000;
int ir2 = (radius - thickness) * (radius - thickness);
int or2 = radius * radius;
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
y2StartFound = true;
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;
}
} 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;
}
} else {
if (y1StartFound && !y1EndFound) {//higher line end found
y1EndFound = true;
y1e = y - 1;
drawFastVLine(cx + x, cy + y1s, y - y1s, color);
if (y < 0) {
y = abs(y); // skip the empty middle
}
else
break;
} else if (y2StartFound) {
if (y2EndSearching) {
// we found the end of the lower line after pixel by pixel search
drawFastVLine(cx + x, cy + y2s, y - y2s, color);
y2EndSearching = false;
break;
} else {
// 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;
}
}
}
}
if (y1StartFound && !y1EndFound){
y1e = ymax;
drawFastVLine(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(cx + x, cy + y2s, ymax - y2s + 1, color);
}
}
}
}
/**************************************************************************/
/*!
this update the RA8875 Active Window registers
[private]
*/
/**************************************************************************/
void RA8875::_updateActiveWindow(bool full)
{
if (full){
// X
_writeRegister(RA8875_HSAW0, 0x00);
_writeRegister(RA8875_HSAW0 + 1,0x00);
_writeRegister(RA8875_HEAW0, (RA8875_WIDTH) & 0xFF);
_writeRegister(RA8875_HEAW0 + 1,(RA8875_WIDTH) >> 8);
// Y
_writeRegister(RA8875_VSAW0, 0x00);
_writeRegister(RA8875_VSAW0 + 1,0x00);
_writeRegister(RA8875_VEAW0, (RA8875_HEIGHT) & 0xFF);
_writeRegister(RA8875_VEAW0 + 1,(RA8875_HEIGHT) >> 8);
} else {
// X
_writeRegister(RA8875_HSAW0, _activeWindowXL & 0xFF);
_writeRegister(RA8875_HSAW0 + 1,_activeWindowXL >> 8);
_writeRegister(RA8875_HEAW0, _activeWindowXR & 0xFF);
_writeRegister(RA8875_HEAW0 + 1,_activeWindowXR >> 8);
// Y
_writeRegister(RA8875_VSAW0, _activeWindowYT & 0xFF);
_writeRegister(RA8875_VSAW0 + 1,_activeWindowYT >> 8);
_writeRegister(RA8875_VEAW0, _activeWindowYB & 0xFF);
_writeRegister(RA8875_VEAW0 + 1,_activeWindowYB >> 8);
}
}
/**************************************************************************/
/*!
Graphic line addressing helper
[private]
*/
/**************************************************************************/
void RA8875::_line_addressing(int16_t x0, int16_t y0, int16_t x1, int16_t y1)
{
//X0
_writeRegister(RA8875_DLHSR0, x0 & 0xFF);
_writeRegister(RA8875_DLHSR0 + 1,x0 >> 8);
//Y0
_writeRegister(RA8875_DLVSR0, y0 & 0xFF);
_writeRegister(RA8875_DLVSR0 + 1,y0 >> 8);
//X1
_writeRegister(RA8875_DLHER0, x1 & 0xFF);
_writeRegister(RA8875_DLHER0 + 1,x1 >> 8);
//Y1
_writeRegister(RA8875_DLVER0, y1 & 0xFF);
_writeRegister(RA8875_DLVER0 + 1,y1 >> 8);
}
/**************************************************************************/
/*!
curve addressing helper
[private]
*/
/**************************************************************************/
void RA8875::_curve_addressing(int16_t x0, int16_t y0, int16_t x1, int16_t y1)
{
//center
_writeRegister(RA8875_DEHR0, x0 & 0xFF);
_writeRegister(RA8875_DEHR0 + 1,x0 >> 8);
_writeRegister(RA8875_DEVR0, y0 & 0xFF);
_writeRegister(RA8875_DEVR0 + 1,y0 >> 8);
//long,short ax
_writeRegister(RA8875_ELL_A0, x1 & 0xFF);
_writeRegister(RA8875_ELL_A0 + 1,x1 >> 8);
_writeRegister(RA8875_ELL_B0, y1 & 0xFF);
_writeRegister(RA8875_ELL_B0 + 1,y1 >> 8);
}
/**************************************************************************/
/*!
sin e cos helpers
[private]
*/
/**************************************************************************/
float RA8875::_cosDeg_helper(float angle)
{
float radians = angle / (float)360 * 2 * PI;
return cos(radians);
}
float RA8875::_sinDeg_helper(float angle)
{
float radians = angle / (float)360 * 2 * PI;
return sin(radians);
}
/**************************************************************************/
/*!
change the arc default parameters
*/
/**************************************************************************/
void RA8875::setArcParams(float arcAngleMax, int arcAngleOffset)
{
_arcAngle_max = arcAngleMax;
_arcAngle_offset = arcAngleOffset;
}
/**************************************************************************/
/*!
change the angle offset parameter from default one
*/
/**************************************************************************/
void RA8875::setAngleOffset(int16_t angleOffset)
{
_angle_offset = ANGLE_OFFSET + angleOffset;
}
/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ PWM STUFF +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/**************************************************************************/
/*!
on/off GPIO (basic for Adafruit module
*/
/**************************************************************************/
void RA8875::GPIOX(boolean on)
{
_writeRegister(RA8875_GPIOX, on);
}
/**************************************************************************/
/*!
PWM out
Parameters:
pw: pwm selection (1,2)
p: 0...255 rate
*/
/**************************************************************************/
void RA8875::PWMout(uint8_t pw,uint8_t p)
{
uint8_t reg;
pw > 1 ? reg = RA8875_P2DCR : reg = RA8875_P1DCR;
_writeRegister(reg, p);
}
/**************************************************************************/
/*!
Set the brightness of the backlight (if connected to pwm)
(basic controls pwm 1)
Parameters:
val: 0...255
*/
/**************************************************************************/
void RA8875::brightness(uint8_t val)
{
_brightness = val;
PWMout(1,_brightness);
}
/**************************************************************************/
/*!
controls the backligh by using PWM engine.
It handles adafruit board separately
Parameters:
on: true(backlight on), false(backlight off)
*/
/**************************************************************************/
void RA8875::backlight(boolean on) //0.69b31 (fixed an issue with adafruit backlight)
{
if (_displaySize == Adafruit_480x272 || _displaySize == Adafruit_800x480) GPIOX(on);
if (on == true){
PWMsetup(1,true, RA8875_PWM_CLK_DIV1024);//setup PWM ch 1 for backlight
PWMout(1,_brightness);//turn on PWM1
} else {
PWMsetup(1,false, RA8875_PWM_CLK_DIV1024);//setup PWM ch 1 for backlight
}
}
/**************************************************************************/
/*!
Setup PWM engine
Parameters:
pw: pwm selection (1,2)
on: turn on/off
clock: the clock setting
[private]
*/
/**************************************************************************/
void RA8875::PWMsetup(uint8_t pw,boolean on, uint8_t clock)
{
uint8_t reg;
uint8_t set;
if (pw > 1){
reg = RA8875_P2CR;
on == true ? set = RA8875_PxCR_ENABLE : set = RA8875_PxCR_DISABLE;
} else {
reg = RA8875_P1CR;
on == true ? set = RA8875_PxCR_ENABLE : set = RA8875_PxCR_DISABLE;
}
_writeRegister(reg,(set | (clock & 0xF)));
}
/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ ISR +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
void RA8875::setInternalInt(enum RA8875intlist b)
{
_enabledInterrups |= (1 << b);//set
}
void RA8875::clearInternalInt(enum RA8875intlist b)
{
_enabledInterrups &= ~(1 << b);//clear
}
/**************************************************************************/
/*!
RA8875 can use the INT pin for many purposes.
This function just tell the CPU to use a pin for listen to RA8875 pin,
no matter if we will use ISR or DigitalRead
------------------------------
Bit: Called by: In use:
--------------------------------
0: isr triggered [*]
1: Resistive TS [*]
2: KeyScan [*]
3: DMA
4: BTE
5: -na-
6: -na-
7: -na-
--------------------------------
Parameters:
INTpin: it's the pin where we listen to ISR
INTnum: it's the INT related to pin. On some processor it's not needed
This last parameter it's used only when decide to use an ISR.
*/
/**************************************************************************/
void RA8875::useINT(const uint8_t INTpin,const uint8_t INTnum)
{
_intPin = INTpin;
_intNum = INTnum;
#if defined(___TEENSYES)//all of them (32 bit only)
pinMode(_intPin ,INPUT_PULLUP);
#else
pinMode(_intPin ,INPUT);
#endif
}
/**************************************************************************/
/*!
the generic ISR routine, will set to 1 bit 0 of _RA8875_INTS
[private]
*/
/**************************************************************************/
void RA8875::_isr(void)
{
if(_active_touch_objects[0]) _active_touch_objects[0]->_RA8875_INTS |= (1 << 0);//set
}
void RA8875::_isr1(void)
{
if(_active_touch_objects[1]) _active_touch_objects[1]->_RA8875_INTS |= (1 << 0);//set
}
void RA8875::_isr2(void)
{
if(_active_touch_objects[2]) _active_touch_objects[2]->_RA8875_INTS |= (1 << 0);//set
}
/**************************************************************************/
/*!
Enable the ISR, after this any falling edge on
_intPin pin will trigger ISR
Parameters:
force: if true will force attach interrupt
NOTE:
if parameter _needISRrearm = true will rearm interrupt
*/
/**************************************************************************/
#define COUNT_TOUCH_OBJECTS (sizeof(_active_touch_objects)/sizeof(_active_touch_objects[0]))
void RA8875::enableISR(bool force)
{
static void (*touch_object_isr_ptrs[])(void) = {&_isr, &_isr1, &_isr2};
if (force || _needISRrearm){
uint8_t index_touch_object = 0;
for (; index_touch_object < COUNT_TOUCH_OBJECTS; index_touch_object++) {
if ((_active_touch_objects[index_touch_object] == nullptr) || (_active_touch_objects[index_touch_object] == this)) break;
}
if (index_touch_object == COUNT_TOUCH_OBJECTS) return; // failed to find a free index...
_active_touch_objects[index_touch_object] = this; // claim this spot.
_needISRrearm = false;
#ifdef digitalPinToInterrupt
attachInterrupt(digitalPinToInterrupt(_intPin),touch_object_isr_ptrs[index_touch_object],FALLING);
#else
attachInterrupt(_intNum,touch_object_isr_ptrs[index_touch_object],FALLING);
#endif
_RA8875_INTS = 0b00000000;//reset all INT bits flags
_useISR = true;
}
#if defined(USE_RA8875_TOUCH)
if (_touchEnabled) _checkInterrupt(2);//clear internal RA int to engage
#endif
}
/**************************************************************************/
/*!
Disable ISR
Works only if previously enabled or do nothing.
*/
/**************************************************************************/
void RA8875::_disableISR(void)
{
if (_useISR){
#if defined(USE_RA8875_TOUCH)
if (_touchEnabled) _checkInterrupt(2);//clear internal RA int to engage
#endif
#ifdef digitalPinToInterrupt
detachInterrupt(digitalPinToInterrupt(_intPin));
#else
detachInterrupt(_intNum);
#endif
_RA8875_INTS = 0b00000000;//reset all bits
_useISR = false;
}
}
/**************************************************************************/
/*!
Check the [interrupt register] for an interrupt,
if found it will reset it.
bit
0: complicated....
1: BTE INT
2: TOUCH INT
3: DMA INT
4: Keyscan INT
*/
/**************************************************************************/
bool RA8875::_checkInterrupt(uint8_t _bit,bool _clear)
{
if (_bit > 4) _bit = 4;
uint8_t temp = _readRegister(RA8875_INTC2);
if (bitRead(temp,_bit) == 1){
if (_clear){
temp |= (1 << _bit);//bitSet(temp,_bit);
//if (bitRead(temp,0)) bitSet(temp,0);//Mmmmm...
_writeRegister(RA8875_INTC2, temp);//clear int
}
return true;
}
return false;
}
#if defined(USE_FT5206_TOUCH)
/**************************************************************************/
/*!
The FT5206 Capacitive Touch driver uses a different INT pin than RA8875
and it's not controlled by RA chip of course, so we need a separate code
for that purpose, no matter we decide to use an ISR or digitalRead.
no matter if we will use ISR or DigitalRead
Parameters:
INTpin: it's the pin where we listen to ISR
INTnum: it's the INT related to pin. On some processor it's not needed
This last parameter it's used only when decide to use an ISR.
*/
/**************************************************************************/
void RA8875::useCapINT(const uint8_t INTpin,const uint8_t INTnum)
{
_intCTSPin = INTpin;
_intCTSNum = INTnum;
#if defined(___TEENSYES)//all of them (32 bit only)
pinMode(_intCTSPin ,INPUT_PULLUP);
#else
pinMode(_intCTSPin ,INPUT);
#endif
}
/**************************************************************************/
/*!
Since FT5206 uses a different INT pin, we need a separate isr routine.
[private]
*/
/**************************************************************************/
void RA8875::cts_isr(void)
{
_FT5206_INT = true;
}
/**************************************************************************/
/*!
Enable the ISR, after this any falling edge on
_intCTSPin pin will trigger ISR
Parameters:
force: if true will force attach interrup
NOTE:
if parameter _needCTS_ISRrearm = true will rearm interrupt
*/
/**************************************************************************/
void RA8875::enableCapISR(bool force)
{
if (force || _needCTS_ISRrearm){
_needCTS_ISRrearm = false;
#ifdef digitalPinToInterrupt
attachInterrupt(digitalPinToInterrupt(_intCTSPin),cts_isr,FALLING);
#else
attachInterrupt(_intCTSNum,cts_isr,FALLING);
#endif
_FT5206_INT = false;
_useISR = true;
}
}
/**************************************************************************/
/*!
Disable ISR [FT5206 version]
Works only if previously enabled or do nothing.
*/
/**************************************************************************/
void RA8875::_disableCapISR(void)
{
if (_useISR){
#ifdef digitalPinToInterrupt
detachInterrupt(digitalPinToInterrupt(_intCTSPin));
#else
detachInterrupt(_intCTSNum);
#endif
_FT5206_INT = false;
_useISR = false;
}
}
#endif
/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ TOUCH SCREEN COMMONS +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
#if !defined(_AVOID_TOUCHSCREEN)//Common touch screend methods (for capacitive and resistive)
/**************************************************************************/
/*!
Checks an interrupt has occurred. return true if yes.
Designed to run in loop.
It works with ISR or DigitalRead methods
Parameters:
safe: true (detach interrupt routine, has to be re-engaged manually!)
false (
*/
/**************************************************************************/
bool RA8875::touched(bool safe)
{
if (_useISR){//using interrupts
#if defined(USE_FT5206_TOUCH)
_needCTS_ISRrearm = safe;
if (_FT5206_INT) {
#elif defined(USE_RA8875_TOUCH)
_needISRrearm = safe;
if (bitRead(_RA8875_INTS,0)){
#endif
//there was an interrupt
#if defined(USE_FT5206_TOUCH)
if (_needCTS_ISRrearm){//safe was true, detach int
_disableCapISR();
#else
if (_needISRrearm){//safe was true, detach int
_disableISR();
#endif
} else {//safe was false, do not detatch int and clear INT flag
#if defined(USE_FT5206_TOUCH)
_FT5206_INT = false;
#elif defined(USE_RA8875_TOUCH)
_RA8875_INTS &= ~(1 << 0);//clear
//_checkInterrupt(2);//clear internal RA int to re-engage
#endif
}
return true;
}
return false;
} else {//not use ISR, digitalRead method
#if defined(USE_FT5206_TOUCH)
//TODO
#elif defined(USE_RA8875_TOUCH)
if (_touchEnabled){
#if defined(___TEENSYES)
if (!digitalReadFast(_intPin)) {
#else
if (!digitalRead(_intPin)) {
#endif
_clearTInt = true;
if (_checkInterrupt(2)){
return true;
} else {
return false;
}
}//digitalRead
if (_clearTInt){
_clearTInt = false;
_checkInterrupt(2);//clear internal RA int
delay(1);
}
return false;
} else {//_touchEnabled
return false;
}
#endif
return false;
}
}
void RA8875::setTouchLimit(uint8_t limit)
{
#if defined(USE_FT5206_TOUCH)
if (limit > 5) limit = 5;//max 5 allowed
#else
limit = 1;//always 1
#endif
_maxTouch = limit;
}
uint8_t RA8875::getTouchLimit(void)
{
return _maxTouch;
}
/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ CAPACITIVE TOUCH SCREEN CONTROLLER FT5206 +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
#if defined(USE_FT5206_TOUCH)
/**************************************************************************/
/*!
Initialization sequence of the FT5206
*/
/**************************************************************************/
void RA8875::_initializeFT5206(void)
{
uint8_t i;
for (i=0x80;i<0x89;i++){
_sendRegFT5206(i,_FT5206REgisters[i-0x80]);
}
_sendRegFT5206(0x00,0x00);//Device Mode
}
/**************************************************************************/
/*!
Communicate with FT5206
*/
/**************************************************************************/
void RA8875::_sendRegFT5206(uint8_t reg,const uint8_t val)
{
// save I2C bitrate
#if !defined(___DUESTUFF)
uint8_t twbrbackup = TWBR;
TWBR = 12; // upgrade to 400KHz!
#endif
_wire->beginTransmission(_ctpAdrs);
_wire->write(reg);
_wire->write(val);
_wire->endTransmission(_ctpAdrs);
#if !defined(___DUESTUFF)
TWBR = twbrbackup;
#endif
}
/**************************************************************************/
/*!
This is the function that update the current state of the Touch Screen
It's developed for use in loop
*/
/**************************************************************************/
void RA8875::updateTS(void)
{
_wire->requestFrom((uint8_t)_ctpAdrs,(uint8_t)31); //get 31 registers
uint8_t index = 0;
while(_wire->available()) {
_cptRegisters[index++] = _wire->read();//fill registers
}
_currentTouches = _cptRegisters[0x02] & 0xF;
if (_currentTouches > _maxTouch) _currentTouches = _maxTouch;
_gesture = _cptRegisters[0x01];
if (_maxTouch < 2) _gesture = 0;
uint8_t temp = _cptRegisters[0x03];
_currentTouchState = 0;
if (!bitRead(temp,7) && bitRead(temp,6)) _currentTouchState = 1;//finger up
if (bitRead(temp,7) && !bitRead(temp,6)) _currentTouchState = 2;//finger down
}
/**************************************************************************/
/*!
It gets coordinates out from data collected by updateTS function
Actually it will not communicate with FT5206, just reorder collected data
so it MUST be used after updateTS!
*/
/**************************************************************************/
uint8_t RA8875::getTScoordinates(uint16_t (*touch_coordinates)[2])
{
uint8_t i;
if (_currentTouches < 1) return 0;
for (i=1;i<=_currentTouches;i++){
switch(_rotation){
case 0://ok
//touch_coordinates[i-1][0] = _width - (((_cptRegisters[coordRegStart[i-1]] & 0x0f) << 8) | _cptRegisters[coordRegStart[i-1] + 1]) / (4096/_width);
//touch_coordinates[i-1][1] = (((_cptRegisters[coordRegStart[i-1]] & 0x0f) << 8) | _cptRegisters[coordRegStart[i-1] + 1]) / (4096/_height);
touch_coordinates[i-1][0] = ((_cptRegisters[coordRegStart[i-1]] & 0x0f) << 8) | _cptRegisters[coordRegStart[i-1] + 1];
touch_coordinates[i-1][1] = ((_cptRegisters[coordRegStart[i-1] + 2] & 0x0f) << 8) | _cptRegisters[coordRegStart[i-1] + 3];
break;
case 1://ok
touch_coordinates[i-1][0] = (((_cptRegisters[coordRegStart[i-1] + 2] & 0x0f) << 8) | _cptRegisters[coordRegStart[i-1] + 3]);
touch_coordinates[i-1][1] = (RA8875_WIDTH - 1) - (((_cptRegisters[coordRegStart[i-1]] & 0x0f) << 8) | _cptRegisters[coordRegStart[i-1] + 1]);
break;
case 2://ok
touch_coordinates[i-1][0] = (RA8875_WIDTH - 1) - (((_cptRegisters[coordRegStart[i-1]] & 0x0f) << 8) | _cptRegisters[coordRegStart[i-1] + 1]);
touch_coordinates[i-1][1] = (RA8875_HEIGHT - 1) -(((_cptRegisters[coordRegStart[i-1] + 2] & 0x0f) << 8) | _cptRegisters[coordRegStart[i-1] + 3]);
break;
case 3://ok
touch_coordinates[i-1][0] = (RA8875_HEIGHT - 1) - (((_cptRegisters[coordRegStart[i-1] + 2] & 0x0f) << 8) | _cptRegisters[coordRegStart[i-1] + 3]);
touch_coordinates[i-1][1] = (((_cptRegisters[coordRegStart[i-1]] & 0x0f) << 8) | _cptRegisters[coordRegStart[i-1] + 1]);
break;
}
if (i == _maxTouch) return i;
}
return _currentTouches;
}
/**************************************************************************/
/*!
Gets the current Touch State, must be used AFTER updateTS!
*/
/**************************************************************************/
uint8_t RA8875::getTouchState(void)
{
return _currentTouchState;
}
/**************************************************************************/
/*!
Gets the number of touches, must be used AFTER updateTS!
Return 0..5
0: no touch
*/
/**************************************************************************/
uint8_t RA8875::getTouches(void)
{
return _currentTouches;
}
/**************************************************************************/
/*!
Gets the gesture, if identified, must be used AFTER updateTS!
*/
/**************************************************************************/
uint8_t RA8875::getGesture(void)
{
//if gesture is up, down, left or right, juggle with bit2 & bit3 to match rotation
if((_gesture >> 4) & 1){
switch(_rotation){
case 0:
_gesture ^= 1 << 2;
if(!((_gesture >> 2) & 1)) _gesture ^= 1 << 3;
break;
case 1:
_gesture ^= 1 << 3;
break;
case 2:
_gesture ^= 1 << 2;
if((_gesture >> 2) & 1) _gesture ^= 1 << 3;
break;
}
}
return _gesture;
}
#elif defined(USE_RA8875_TOUCH)
/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ RESISTIVE TOUCH STUFF +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/**************************************************************************/
/*!
Initialize support for on-chip resistive Touch Screen controller
It also enable the Touch Screen
NOTE:
You need to use useINT(pin) [before] to define an INT pin!
You need to use enableISR() [after] to enable ISR on MCU and RA8875!
*/
/**************************************************************************/
void RA8875::touchBegin(void)
{
if (_intPin < 255){
/* Touch Panel Control Register 0 TPCR0 [0x70]
7: 0(disable, 1:(enable)
6,5,4:TP Sample Time Adjusting (000...111)
3:Touch Panel Wakeup Enable 0(disable),1(enable)
2,1,0:ADC Clock Setting (000...111) set fixed to 010: (System CLK) / 4, 10Mhz Max! */
#if defined(___TEENSYES) || defined(___DUESTUFF)//fast 32 bit processors
_writeRegister(RA8875_TPCR0, TP_ENABLE | TP_ADC_SAMPLE_16384_CLKS | TP_ADC_CLKDIV_32);
#else
_writeRegister(RA8875_TPCR0, TP_ENABLE | TP_ADC_SAMPLE_4096_CLKS | TP_ADC_CLKDIV_16);
#endif
_writeRegister(RA8875_TPCR1, TP_MODE_AUTO | TP_DEBOUNCE_ON);
setInternalInt(TOUCH);
_INTC1_Reg |= (1 << 2);//set
_writeRegister(RA8875_INTC1, _INTC1_Reg);
_touchEnabled = true;
} else {
_touchEnabled = false;
}
}
/**************************************************************************/
/*!
Enables or disables the on-chip touch screen controller
You must use touchBegin at list once to instruct the RA8875
Parameters:
enabled: true(enable),false(disable)
*/
/**************************************************************************/
void RA8875::touchEnable(boolean enabled) {
if (_intPin < 255){
/* another grrrr bug of the RA8875!
if we are in text mode the RA chip cannot get back the
INT mode!
*/
if (_textMode) _setTextMode(false);
if (!_touchEnabled && enabled) {//Enable
//enableISR(true);
// bitSet(_INTC1_Reg,2);
// _writeRegister(RA8875_INTC1, _INTC1_Reg);
_touchEnabled = true;
_checkInterrupt(2);
} else if (_touchEnabled && !enabled) {//disable
//_disableISR();
// bitClear(_INTC1_Reg,2);
// _writeRegister(RA8875_INTC1, _INTC1_Reg);
_checkInterrupt(2);
_touchEnabled = false;
}
} else {
_touchEnabled = false;
}
}
/**************************************************************************/
/*!
Read 10bit internal ADC of RA8875 registers and perform corrections
It will return always RAW data
Parameters:
x: out 0...1023
Y: out 0...1023
*/
/**************************************************************************/
void RA8875::readTouchADC(uint16_t *x, uint16_t *y)
{
#if defined(_FASTCPU)
_slowDownSPI(true);
#endif
uint16_t tx = _readRegister(RA8875_TPXH);
uint16_t ty = _readRegister(RA8875_TPYH);
uint8_t remain = _readRegister(RA8875_TPXYL);
#if defined(_FASTCPU)
_slowDownSPI(false);
#endif
tx <<= 2;
ty <<= 2;
tx |= remain & 0x03; // get the bottom x bits
ty |= (remain >> 2) & 0x03; // get the bottom y bits
if (_portrait){
*x = ty;
*y = tx;
} else {
tx = 1024 - tx;
ty = 1024 - ty;
*x = tx;
*y = ty;
}
}
/**************************************************************************/
/*!
Returns 10bit x,y data with TRUE scale (0...1023)
Parameters:
x: out 0...1023
Y: out 0...1023
*/
/**************************************************************************/
void RA8875::touchReadAdc(uint16_t *x, uint16_t *y)
{
uint16_t tx,ty;
readTouchADC(&tx,&ty);
if (_calibrated) {
*x = map(tx,_tsAdcMinX,_tsAdcMaxX,0,1024);
*y = map(ty,_tsAdcMinY,_tsAdcMaxY,0,1024);
} else {
*x = tx;
*y = ty;
}
_checkInterrupt(2);
}
/**************************************************************************/
/*!
Returns pixel x,y data with SCREEN scale (screen width, screen Height)
Parameters:
x: out 0...screen width (pixels)
Y: out 0...screen Height (pixels)
Check for out-of-bounds here as touches near the edge of the screen
can be safely mapped to the nearest point of the screen.
If the screen is rotated, then the min and max will be modified elsewhere
so that this always corresponds to screen pixel coordinates.
/M.SANDERSCROCK added constrain
*/
/**************************************************************************/
void RA8875::touchReadPixel(uint16_t *x, uint16_t *y)
{
uint16_t tx,ty;
readTouchADC(&tx,&ty);
//*x = map(tx,_tsAdcMinX,_tsAdcMaxX,0,_width-1);
*x = constrain(map(tx,_tsAdcMinX,_tsAdcMaxX,0,_width-1),0,_width-1);
//*y = map(ty,_tsAdcMinY,_tsAdcMaxY,0,_height-1);
*y = constrain(map(ty,_tsAdcMinY,_tsAdcMaxY,0,_height-1),0,_height-1);
_checkInterrupt(2);
}
boolean RA8875::touchCalibrated(void)
{
return _calibrated;
}
/**************************************************************************/
/*! A service utility that detects if system has been calibrated in the past
Return true if an old calibration exists
[private]
*/
/**************************************************************************/
boolean RA8875::_isCalibrated(void)
{
uint8_t uncaltetection = 4;
#if defined(TOUCSRCAL_XLOW) && (TOUCSRCAL_XLOW != 0)
uncaltetection--;
#endif
#if defined(TOUCSRCAL_YLOW) && (TOUCSRCAL_YLOW != 0)
uncaltetection--;
#endif
#if defined(TOUCSRCAL_XHIGH) && (TOUCSRCAL_XHIGH != 0)
uncaltetection--;
#endif
#if defined(TOUCSRCAL_YHIGH) && (TOUCSRCAL_YHIGH != 0)
uncaltetection--;
#endif
if (uncaltetection < 4) return true;
return false;
}
/**************************************************************************/
/*! A way to have different calibrations for different displays and
a way to force the system to act like uncalibrated, so you don't have
to edit header file, rebuild, edit header again ...
*/
/**************************************************************************/
void RA8875::setTouchCalibrationData(uint16_t minX, uint16_t maxX, uint16_t minY, uint16_t maxY)
{
_touchrcal_xlow = minX;
_touchrcal_xhigh = maxX;
_touchrcal_ylow = minY;
_touchrcal_yhigh = maxY;
// Lets guess at setting is calibrated.
_calibrated = (minX || maxX) && (minY || maxY);
setRotation(_rotation); // make sure it is updated to what ever the rotation is now.
}
#endif
#endif
/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ SLEEP STUFF +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/**************************************************************************/
/*!
Sleep mode on/off (complete sequence)
The sleep on/off sequence it's quite tricky on RA8875 when in SPI mode!
*/
/**************************************************************************/
void RA8875::sleep(boolean sleep)
{
if (_sleep != sleep){//only if it's needed
_sleep = sleep;
if (_sleep == true){
//1)turn off backlight
if (_displaySize == Adafruit_480x272 || _displaySize == Adafruit_800x480/* || _displaySize == Adafruit_640x480*/) GPIOX(false);
//2)decelerate SPI clock
#if defined(_FASTCPU)
_slowDownSPI(true);
#else
#if defined(SPI_HAS_TRANSACTION)
_SPITransactionSpeed = 4000000UL;
#else
#if defined(___DUESTUFF) && defined(SPI_DUE_MODE_EXTENDED)
SPI.setClockDivider(_cs,SPI_SPEED_READ);
#else
SPI.setClockDivider(SPI_SPEED_READ);
#endif
#endif
#endif
//3)set PLL to default
_setSysClock(0x07,0x03,0x02);
//4)display off & sleep
_writeRegister(RA8875_PWRR, RA8875_PWRR_DISPOFF | RA8875_PWRR_SLEEP);
delay(100);
} else {
//1)wake up with display off(100ms)
_writeRegister(RA8875_PWRR, RA8875_PWRR_DISPOFF);
delay(100);
//2)bring back the pll
_setSysClock(initStrings[_initIndex][0],initStrings[_initIndex][1],initStrings[_initIndex][2]);
//_writeRegister(RA8875_PCSR,initStrings[_initIndex][2]);//Pixel Clock Setting Register
delay(20);
_writeRegister(RA8875_PWRR, RA8875_PWRR_NORMAL | RA8875_PWRR_DISPON);//disp on
delay(20);
//4)resume SPI speed
#if defined(_FASTCPU)
_slowDownSPI(false);
#else
#if defined(SPI_HAS_TRANSACTION)
_SPITransactionSpeed = _SPIMaxSpeed;
#else
#if defined(___DUESTUFF) && defined(SPI_DUE_MODE_EXTENDED)
SPI.setClockDivider(_cs,SPI_SPEED_WRITE);
#else
SPI.setClockDivider(SPI_SPEED_WRITE);
#endif
#endif
#endif
//5)PLL afterburn!
_setSysClock(sysClockPar[_initIndex][0],sysClockPar[_initIndex][1],initStrings[_initIndex][2]);
//5)turn on backlight
if (_displaySize == Adafruit_480x272 || _displaySize == Adafruit_800x480/* || _displaySize == Adafruit_640x480*/) GPIOX(true);
//_writeRegister(RA8875_PWRR, RA8875_PWRR_NORMAL);
}
}
}
/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ SPI & LOW LEVEL STUFF +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/**************************************************************************/
/*! PRIVATE
Write in a register
Parameters:
reg: the register
val: the data
*/
/**************************************************************************/
void RA8875::_writeRegister(const uint8_t reg, uint8_t val)
{
writeCommand(reg);
_writeData(val);
}
/**************************************************************************/
/*! PRIVATE
Returns the value inside register
Parameters:
reg: the register
*/
/**************************************************************************/
uint8_t RA8875::_readRegister(const uint8_t reg)
{
writeCommand(reg);
return _readData(false);
}
/**************************************************************************/
/*!
Write data
Parameters:
d: the data
*/
/**************************************************************************/
void RA8875::_writeData(uint8_t data)
{
_startSend();
#if defined(___DUESTUFF) && defined(SPI_DUE_MODE_EXTENDED)
SPI.transfer(_cs, RA8875_DATAWRITE, SPI_CONTINUE);
SPI.transfer(_cs, data, SPI_LAST);
#else
#if (defined(__AVR__) && defined(_FASTSSPORT)) || defined(SPARK)
_spiwrite(RA8875_DATAWRITE);
_spiwrite(data);
#else
#if defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) || defined(__MKL26Z64__)
_pspi->transfer(RA8875_DATAWRITE);
_pspi->transfer(data);
#else
SPI.transfer(RA8875_DATAWRITE);
SPI.transfer(data);
#endif
#endif
#endif
_endSend();
}
/**************************************************************************/
/*!
Write 16 bit data
Parameters:
d: the data (16 bit)
*/
/**************************************************************************/
void RA8875::writeData16(uint16_t data)
{
_startSend();
#if (defined(__AVR__) && defined(_FASTSSPORT)) || defined(SPARK)
_spiwrite(RA8875_DATAWRITE);
#else
#if defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) || defined(__MKL26Z64__)
_pspi->transfer(RA8875_DATAWRITE);
#else
SPI.transfer(RA8875_DATAWRITE);
#endif
#endif
#if !defined(ENERGIA) && !defined(___DUESTUFF) && ((ARDUINO >= 160) || (TEENSYDUINO > 121))
#if defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) || defined(__MKL26Z64__)
_pspi->transfer16(data);
#else
SPI.transfer16(data);
#endif
#else
#if defined(___DUESTUFF) && defined(SPI_DUE_MODE_EXTENDED)
SPI.transfer(_cs, highByte(data), SPI_CONTINUE);
SPI.transfer(_cs, lowByte(data), SPI_LAST);
#else
#if (defined(__AVR__) && defined(_FASTSSPORT)) || defined(SPARK)
_spiwrite16(data);
#else
SPI.transfer(data >> 8);
SPI.transfer(data & 0xFF);
#endif
#endif
#endif
_endSend();
}
/**************************************************************************/
/*! PRIVATE
*/
/**************************************************************************/
uint8_t RA8875::_readData(bool stat)
{
#if defined(SPI_HAS_TRANSACTION)
//Serial.printf("RA8875::_readData _SPIMaxSpeed: %d\n", _SPIMaxSpeed);
if (_inited) _SPITransactionSpeed = _SPIMaxReadSpeed;
#else
#if defined(___DUESTUFF) && defined(SPI_DUE_MODE_EXTENDED)
if (_inited) SPI.setClockDivider(_cs,SPI_SPEED_READ);
#else
if (_inited) SPI.setClockDivider(SPI_SPEED_READ);
#endif
#endif
_startSend();
#if defined(___DUESTUFF) && defined(SPI_DUE_MODE_EXTENDED)
stat == true ? SPI.transfer(_cs, RA8875_CMDREAD, SPI_CONTINUE) : SPI.transfer(_cs, RA8875_DATAREAD, SPI_CONTINUE);
uint8_t x = SPI.transfer(_cs, 0x0, SPI_LAST);
#else
#if (defined(__AVR__) && defined(_FASTSSPORT)) || defined(SPARK)
stat == true ? _spiwrite(RA8875_CMDREAD) : _spiwrite(RA8875_DATAREAD);
uint8_t x = _spiread();
#else
#if defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) || defined(__MKL26Z64__)
stat == true ? _pspi->transfer(RA8875_CMDREAD) : _pspi->transfer(RA8875_DATAREAD);
uint8_t x = _pspi->transfer(0x0);
#else
stat == true ? SPI.transfer(RA8875_CMDREAD) : SPI.transfer(RA8875_DATAREAD);
uint8_t x = SPI.transfer(0x0);
#endif
#endif
#endif
_endSend();
#if defined(SPI_HAS_TRANSACTION)
if (_inited) _SPITransactionSpeed = _SPIMaxSpeed;
#else
#if defined(___DUESTUFF) && defined(SPI_DUE_MODE_EXTENDED)
if (_inited) SPI.setClockDivider(_cs,SPI_SPEED_WRITE);
#else
if (_inited) SPI.setClockDivider(SPI_SPEED_WRITE);
#endif
#endif
return x;
}
/**************************************************************************/
/*!
*/
/**************************************************************************/
uint8_t RA8875::readStatus(void)
{
return _readData(true);
}
/**************************************************************************/
/*! PRIVATE
Write a command
Parameters:
d: the command
*/
/**************************************************************************/
void RA8875::writeCommand(const uint8_t d)
{
_startSend();
#if defined(___DUESTUFF) && defined(SPI_DUE_MODE_EXTENDED)
SPI.transfer(_cs, RA8875_CMDWRITE, SPI_CONTINUE);
SPI.transfer(_cs, d, SPI_LAST);
#else
#if (defined(__AVR__) && defined(_FASTSSPORT)) || defined(SPARK)
_spiwrite(RA8875_CMDWRITE);
_spiwrite(d);
#else
#if defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) || defined(__MKL26Z64__)
_pspi->transfer(RA8875_CMDWRITE);
_pspi->transfer(d);
#else
SPI.transfer(RA8875_CMDWRITE);
SPI.transfer(d);
#endif
#endif
#endif
_endSend();
}
void RA8875::_fontWrite(const uint8_t* buffer, uint16_t len)
{
if(_use_default) {
if (_FNTgrandient) _FNTgrandient = false;//cannot use this with write
_textWrite((const char *)buffer, len);
//Serial.printf("Default: %c, %d, %d\n", c, _cursorX, _cursorY);
return;
}
// Lets try to handle some of the special font centering code that was done for default fonts.
if (_absoluteCenter || _relativeCenter ) {
int16_t x, y;
uint16_t strngWidth, strngHeight;
getTextBounds(buffer, len, 0, 0, &x, &y, &strngWidth, &strngHeight);
//Serial.printf("_fontwrite bounds: %d %d %u %u\n", x, y, strngWidth, strngHeight);
// Note we may want to play with the x ane y returned if they offset some
if (_absoluteCenter && strngWidth > 0){//Avoid operations for strngWidth = 0
_absoluteCenter = false;
_cursorX = _cursorX - ((x + strngWidth) / 2);
_cursorY = _cursorY - ((y + strngHeight) / 2);
} else if (_relativeCenter && strngWidth > 0){//Avoid operations for strngWidth = 0
_relativeCenter = false;
if (bitRead(_TXTparameters,5)) {//X = center
_cursorX = (_width / 2) - ((x + strngWidth) / 2);
_TXTparameters &= ~(1 << 5);//reset
}
if (bitRead(_TXTparameters,6)) {//Y = center
_cursorY = (_height / 2) - ((y + strngHeight) / 2) ;
_TXTparameters &= ~(1 << 6);//reset
}
}
}
while(len) {
uint8_t c = *buffer++;
if (font) {
//Serial.printf("ILI: %c, %d, %d\n", c, _cursorX, _cursorY);
if (c == '\n') {
//_cursorY += font->line_space;
//_cursorX = 0;
} else {
if (c == 13) {
_cursorY += font->line_space;
_cursorX = 0;
} else {
drawFontChar(c);
}
}
} else if (gfxFont) {
//Serial.printf("GFX: %c, %d, %d\n", c, _cursorX, _cursorY);
if (c == '\n') {
_cursorY += (int16_t)textsize_y * gfxFont->yAdvance;
_cursorX = 0;
} else {
drawGFXFontChar(c);
}
} else {
if (c == '\n') {
_cursorY += textsize_y*8;
_cursorX = 0;
} else if (c == '\r') {
// skip em
} else {
drawChar(_cursorX, _cursorY, c, textcolor, textbgcolor, textsize_x, textsize_y);
_cursorX += textsize_x*6;
if (wrap && (_cursorX > (_width - textsize_x*6))) {
_cursorY += textsize_y*6;
_cursorX = 0;
}
}
}
len--;
}
}
//#include "glcdfont.c"
extern "C" const unsigned char glcdfont[];
// Draw a character
void RA8875::drawChar(int16_t x, int16_t y, unsigned char c,
uint16_t fgcolor, uint16_t bgcolor, uint8_t size_x, uint8_t size_y)
{
if((x >= _width) || // Clip right
(y >= _height) || // Clip bottom
((x + 6 * size_x - 1) < 0) || // Clip left TODO: is this correct?
((y + 8 * size_y - 1) < 0)) // Clip top TODO: is this correct?
return;
//Serial.printf("drawchar %d %d %c %x %x %d %d\n", x, y, c, fgcolor, bgcolor, size_x, size_y);
if (fgcolor == bgcolor) {
// This transparent approach is only about 20% faster
if ((size_x == 1) && (size_y == 1)) {
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 (glcdfont[c * 5 + xoff] & mask) line |= 1;
line <<= 1;
}
line >>= 1;
xoff = 0;
while (line) {
if (line == 0x1F) {
drawFastHLine(x + xoff, y + yoff, 5, fgcolor);
break;
} else if (line == 0x1E) {
drawFastHLine(x + xoff, y + yoff, 4, fgcolor);
break;
} else if ((line & 0x1C) == 0x1C) {
drawFastHLine(x + xoff, y + yoff, 3, fgcolor);
line <<= 4;
xoff += 4;
} else if ((line & 0x18) == 0x18) {
drawFastHLine(x + xoff, y + yoff, 2, fgcolor);
line <<= 3;
xoff += 3;
} else if ((line & 0x10) == 0x10) {
drawPixel(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 (glcdfont[c * 5 + xoff] & mask) line |= 1;
line <<= 1;
}
line >>= 1;
xoff = 0;
while (line) {
if (line == 0x1F) {
fillRect(x + xoff * size_x, y + yoff * size_y,
5 * size_x, size_y, fgcolor);
break;
} else if (line == 0x1E) {
fillRect(x + xoff * size_x, y + yoff * size_y,
4 * size_x, size_y, fgcolor);
break;
} else if ((line & 0x1C) == 0x1C) {
fillRect(x + xoff * size_x, y + yoff * size_y,
3 * size_x, size_y, fgcolor);
line <<= 4;
xoff += 4;
} else if ((line & 0x18) == 0x18) {
fillRect(x + xoff * size_x, y + yoff * size_y,
2 * size_x, size_y, fgcolor);
line <<= 3;
xoff += 3;
} else if ((line & 0x10) == 0x10) {
fillRect(x + xoff * size_x, y + yoff * size_y,
size_x, size_y, fgcolor);
line <<= 2;
xoff += 2;
} else {
line <<= 1;
xoff += 1;
}
}
mask = mask << 1;
}
}
} else {
// This solid background approach is about 5 time faster
uint8_t xc, yc;
uint8_t xr, yr;
uint8_t mask = 0x01;
// We need to offset by the origin.
x+=_originx;
y+=_originy;
int16_t x_char_start = x; // remember our X where we start outputting...
if((x >= _displayclipx2) || // Clip right
(y >= _displayclipy2) || // Clip bottom
((x + 6 * size_x - 1) < _displayclipx1) || // Clip left TODO: this is not correct
((y + 8 * size_y - 1) < _displayclipy1)) // Clip top TODO: this is not correct
return;
// need to build actual pixel rectangle we will output into.
int16_t y_char_top = y; // remember the y
int16_t w = 6 * size_x;
int16_t h = 8 * size_y;
if(x < _displayclipx1) { w -= (_displayclipx1-x); x = _displayclipx1; }
if((x + w - 1) >= _displayclipx2) w = _displayclipx2 - x;
if(y < _displayclipy1) { h -= (_displayclipy1 - y); y = _displayclipy1; }
if((y + h - 1) >= _displayclipy2) h = _displayclipy2 - y;
//Serial.printf("%d, %d, %d, %d\n", x, y, x + w -1, y + h - 1);
setActiveWindow(x, y, x + w -1, y + h - 1);
_startSend();
y = y_char_top; // restore the actual y.
writeCommand(RA8875_MRWC);
for (yc=0; (yc < 8) && (y < _displayclipy2); yc++) {
for (yr=0; (yr < size_y) && (y < _displayclipy2); yr++) {
x = x_char_start; // get our first x position...
if (y >= _displayclipy1) {
for (xc=0; xc < 5; xc++) {
for (xr=0; xr < size_x; xr++) {
if ((x >= _displayclipx1) && (x < _displayclipx2)) {
//write16BitColor(fgcolor);
drawPixel(xr+x,yc+y,fgcolor);
}
x++;
}
}
for (xr=0; xr < size_x; xr++) {
if ((x >= _displayclipx1) && (x < _displayclipx2)) {
//write16BitColor(bgcolor);
drawPixel(xr+x,yc+y,bgcolor);
}
x++;
}
}
y++;
}
mask = mask << 1;
}
//writecommand_last(ILI9488_NOP);
_endSend();
}
}
void RA8875::setFont(const ILI9341_t3_font_t &f) {
_use_default = 0;
if(_portrait && !_use_gfx_font) {
_cursorY += _cursorX;
_cursorX -= _cursorY;
}
_use_ili_font = 1;
_use_gfx_font = 0;
_use_int_font = 1;
_use_tfont = 0;
_gfx_last_char_x_write = 0; // Don't use cached data here
font = &f;
if (gfxFont) {
_cursorY -= 6;
gfxFont = NULL;
}
fontbpp = 1;
// Calculate additional metrics for Anti-Aliased font support (BDF extn v2.3)
if (font && font->version==23){
fontbpp = (font->reserved & 0b000011)+1;
fontbppindex = (fontbpp >> 2)+1;
fontbppmask = (1 << (fontbppindex+1))-1;
fontppb = 8/fontbpp;
fontalphamx = 31/((1<last - f->first); i++) {
if (f->glyph[i].yOffset < miny_offset) {
miny_offset = f->glyph[i].yOffset;
}
}
#else
int max_delta = 0;
uint8_t index_min = 0;
uint8_t index_max = 0;
int8_t minx_offset = 127;
int8_t maxx_overlap = 0;
uint8_t indexx_min = 0;
uint8_t indexx_max = 0;
for (uint8_t i=0; i <= (f->last - f->first); i++) {
if (f->glyph[i].yOffset < miny_offset) {
miny_offset = f->glyph[i].yOffset;
index_min = i;
}
if (f->glyph[i].xOffset < minx_offset) {
minx_offset = f->glyph[i].xOffset;
indexx_min = i;
}
if ( (f->glyph[i].yOffset + f->glyph[i].height) > max_delta) {
max_delta = (f->glyph[i].yOffset + f->glyph[i].height);
index_max = i;
}
int8_t x_overlap = f->glyph[i].xOffset + f->glyph[i].width - f->glyph[i].xAdvance;
if (x_overlap > maxx_overlap) {
maxx_overlap = x_overlap;
indexx_max = i;
}
}
Serial.printf("Set GFX Font(%x): Y: %d %d(%c) %d(%c) X: %d(%c) %d(%c)\n", (uint32_t)f, f->yAdvance,
miny_offset, index_min + f->first, max_delta, index_max + f->first,
minx_offset, indexx_min + f->first, maxx_overlap, indexx_max + f->first);
#endif
_gfxFont_min_yOffset = miny_offset; // Probably only thing we need... May cache?
} else if(gfxFont) { // NULL passed. Current font struct defined?
// Switching from new to classic font behavior.
// Move cursor pos up 6 pixels so it's at top-left of char.
_cursorY -= 6;
}
gfxFont = f;
}
void RA8875::drawFontChar(unsigned int c)
{
uint32_t bitoffset = 0;
const uint8_t *data;
//Serial.printf("drawFontChar(%c) %d (%d, %d) %x %x %x\n", c, c, _cursorX, _cursorY, _TXTBackColor, _TXTForeColor, _backTransparent);
if (c >= font->index1_first && c <= font->index1_last) {
bitoffset = c - font->index1_first;
bitoffset *= font->bits_index;
} else if (c >= font->index2_first && c <= font->index2_last) {
bitoffset = c - font->index2_first + font->index1_last - font->index1_first + 1;
bitoffset *= font->bits_index;
}
// TODO: implement sparse unicode
//Serial.printf(" index = %d\n", fetchbits_unsigned(font->index, bitoffset, font->bits_index));
data = font->data + fetchbits_unsigned(font->index, bitoffset, font->bits_index);
uint32_t encoding = fetchbits_unsigned(data, 0, 3);
if (encoding != 0) return;
uint32_t width = fetchbits_unsigned(data, 3, font->bits_width);
bitoffset = font->bits_width + 3;
uint32_t height = fetchbits_unsigned(data, bitoffset, font->bits_height);
bitoffset += font->bits_height;
//Serial.printf(" size = %d,%d\n", width, height);
//Serial.printf(" line space = %d\n", font->line_space);
//Serial.printf(" cap height = %d\n", font->cap_height);
int32_t xoffset = fetchbits_signed(data, bitoffset, font->bits_xoffset);
bitoffset += font->bits_xoffset;
int32_t yoffset = fetchbits_signed(data, bitoffset, font->bits_yoffset);
bitoffset += font->bits_yoffset;
//Serial.printf(" offset = %d,%d\n", xoffset, yoffset);
uint32_t delta = fetchbits_unsigned(data, bitoffset, font->bits_delta);
bitoffset += font->bits_delta;
//Serial.printf(" delta = %d\n", delta);
//horizontally, we draw every pixel, or none at all
if (_cursorX < 0) _cursorX = 0;
int32_t origin_x = _cursorX + xoffset;
if (origin_x < 0) {
_cursorX -= xoffset;
origin_x = 0;
}
if (origin_x + (int)width > _width) {
if (!wrap) return;
origin_x = 0;
if (xoffset >= 0) {
_cursorX = 0;
} else {
_cursorX = -xoffset;
}
_cursorY += font->line_space;
}
if (_cursorY >= _height) return;
// vertically, the top and/or bottom can be clipped
int32_t origin_y = _cursorY + font->cap_height - height - yoffset;
// TODO: compute top skip and number of lines
int32_t linecount = height;
//uint32_t loopcount = 0;
int32_t y = origin_y;
bool opaque = !_backTransparent; //(_TXTBackColor != _TXTForeColor);
// Going to try a fast Opaque method which works similar to drawChar, which is near the speed of writerect
if (!opaque) {
// Anti-alias support
if (fontbpp>1){
// This branch should, in most cases, never happen. This is because if an
// anti-aliased font is being used, textcolor and textbgcolor should always
// be different. Even though an anti-alised font is being used, pixels in this
// case will all be solid because pixels are rendered on same colour as themselves!
// This won't look very good.
bitoffset = ((bitoffset + 7) & (-8)); // byte-boundary
uint32_t xp = 0;
uint8_t halfalpha = 1<<(fontbpp-1);
while (linecount) {
uint32_t x = 0;
while(x 0.5) or off
if (fetchpixel(data, bitoffset, xp)>=halfalpha){
Pixel(origin_x + x,y, _TXTForeColor);
}
bitoffset += fontbpp;
x++;
xp++;
}
y++;
linecount--;
}
}
// Soild pixels
else {
while (linecount > 0) {
//Serial.printf(" linecount = %d\n", linecount);
uint32_t n = 1;
if (fetchbit(data, bitoffset++) != 0) {
n = fetchbits_unsigned(data, bitoffset, 3) + 2;
bitoffset += 3;
}
uint32_t x = 0;
do {
int32_t xsize = width - x;
if (xsize > 32) xsize = 32;
uint32_t bits = fetchbits_unsigned(data, bitoffset, xsize);
//Serial.printf(" multi line %d %d %x\n", n, x, bits);
drawFontBits(opaque, bits, xsize, origin_x + x, y, n);
bitoffset += xsize;
x += xsize;
} while (x < width);
y += n;
linecount -= n;
//if (++loopcount > 100) {
//Serial.println(" abort draw loop");
//break;
//}
}
}
} else { //Opaque
if (fontbpp>1){
// Now opaque mode...
// Now write out background color for the number of rows above the above the character
// figure out bounding rectangle...
// In this mode we need to update to use the offset and bounding rectangles as we are doing it it direct.
// also update the Origin
int cursor_x_origin = _cursorX + _originx;
int cursor_y_origin = _cursorY + _originy;
_cursorX += _originx;
_cursorY += _originy;
int start_x = (origin_x < cursor_x_origin) ? origin_x : cursor_x_origin;
if (start_x < 0) start_x = 0;
int start_y = (origin_y < cursor_y_origin) ? origin_y : cursor_y_origin;
if (start_y < 0) start_y = 0;
int end_x = cursor_x_origin + delta;
if ((origin_x + (int)width) > end_x)
end_x = origin_x + (int)width;
if (end_x >= _displayclipx2) end_x = _displayclipx2;
int end_y = cursor_y_origin + font->line_space;
if ((origin_y + (int)height) > end_y)
end_y = origin_y + (int)height;
if (end_y >= _displayclipy2) end_y = _displayclipy2;
end_x--; // setup to last one we draw
end_y--;
int start_x_min = (start_x >= _displayclipx1) ? start_x : _displayclipx1;
int start_y_min = (start_y >= _displayclipy1) ? start_y : _displayclipy1;
// See if anything is in the display area.
if((end_x < _displayclipx1) ||(start_x >= _displayclipx2) || (end_y < _displayclipy1) || (start_y >= _displayclipy2)) {
_cursorX += delta; // could use goto or another indent level...
return;
}
/*
Serial.printf("drawFontChar(%c) %d\n", c, c);
Serial.printf(" size = %d,%d\n", width, height);
Serial.printf(" line space = %d\n", font->line_space);
Serial.printf(" offset = %d,%d\n", xoffset, yoffset);
Serial.printf(" delta = %d\n", delta);
Serial.printf(" cursor = %d,%d\n", _cursorX, _cursorY);
Serial.printf(" origin = %d,%d\n", origin_x, origin_y);
Serial.printf(" Bounding: (%d, %d)-(%d, %d)\n", start_x, start_y, end_x, end_y);
Serial.printf(" mins (%d %d),\n", start_x_min, start_y_min);
*/
//}
// Anti-aliased font
//Serial.printf("SetAddr %d %d %d %d\n", start_x_min, start_y_min, end_x, end_y);
// output rectangle we are updating... We have already clipped end_x/y, but not yet start_x/y
setActiveWindow( start_x, start_y_min, end_x, end_y);
int screen_y = start_y_min;
int screen_x;
// Clear above character
while (screen_y < origin_y) {
for (screen_x = start_x_min; screen_x <= end_x; screen_x++) {
drawPixel(screen_x, screen_y, _TXTBackColor);
}
screen_y++;
}
screen_y = origin_y;
bitoffset = ((bitoffset + 7) & (-8)); // byte-boundary
int glyphend_x = origin_x + width;
uint32_t xp = 0;
while (linecount) {
screen_x = start_x;
while(screen_x<=end_x) {
// XXX: I'm sure clipping could be done way more efficiently than just chekcing every single pixel, but let's just get this going
if ((screen_x >= _displayclipx1) && (screen_x < _displayclipx2) && (screen_y >= _displayclipy1) && (screen_y < _displayclipy2)) {
// Clear before or after pixel
if ((screen_x=glyphend_x)){
drawPixel(screen_x, screen_y, _TXTBackColor);
}
// Draw alpha-blended character
else{
uint8_t alpha = fetchpixel(data, bitoffset, xp);
drawPixel(screen_x, screen_y, alphaBlendRGB565Premultiplied( textcolorPrexpanded, textbgcolorPrexpanded, (uint8_t)(alpha * fontalphamx) ) );
bitoffset += fontbpp;
xp++;
}
} // clip
screen_x++;
}
screen_y++;
linecount--;
}
// clear below character
setActiveWindow(); //have to do this otherwise it gets clipped
fillRect(_cursorX, screen_y, delta, _cursorY + font->line_space - screen_y, _TXTBackColor);
} // anti-aliased
// 1bpp
else {
// Now opaque mode...
// Now write out background color for the number of rows above the above the character
// figure out bounding rectangle...
// In this mode we need to update to use the offset and bounding rectangles as we are doing it it direct.
// also update the Origin
fillRect(_cursorX, _cursorY, delta, y - _cursorY, _TXTBackColor);
while (linecount > 0) {
//Serial.printf(" linecount = %d\n", linecount);
uint32_t n = 1;
if (fetchbit(data, bitoffset++) != 0) {
n = fetchbits_unsigned(data, bitoffset, 3) + 2;
bitoffset += 3;
}
uint32_t x = 0;
fillRect(_cursorX, y, origin_x - _cursorX, n, _TXTBackColor);
do {
int32_t xsize = width - x;
if (xsize > 32) xsize = 32;
uint32_t bits = fetchbits_unsigned(data, bitoffset, xsize);
//Serial.printf(" multi line %d %d %x\n", n, x, bits);
drawFontBits(opaque, bits, xsize, origin_x + x, y, n);
bitoffset += xsize;
x += xsize;
} while (x < width);
if ((width+xoffset) < delta) {
fillRect(origin_x + x, y, delta - (width+xoffset), n, _TXTBackColor);
}
y += n;
linecount -= n;
//if (++loopcount > 100) {
//Serial.println(" abort draw loop");
//break;
//}
}
fillRect(_cursorX, y, delta, _cursorY + font->line_space - y, _TXTBackColor);
}
}
// Increment to setup for the next character.
_cursorX += delta;
}
//strPixelLen - gets pixel length of given ASCII string
int16_t RA8875::strPixelLen(const char * str)
{
// Serial.printf("strPixelLen %s\n", str);
if (!str) return(0);
if (gfxFont)
{
// BUGBUG:: just use the other function for now... May do this for all of them...
int16_t x, y;
uint16_t w, h;
getTextBounds(str, _cursorX, _cursorY, &x, &y, &w, &h);
return w;
}
uint16_t len=0, maxlen=0;
while (*str)
{
if (*str=='\n')
{
if ( len > maxlen )
{
maxlen=len;
len=0;
}
}
else
{
if (!font)
{
len+=textsize_x*6;
}
else
{
uint32_t bitoffset;
const uint8_t *data;
uint16_t c = *str;
// Serial.printf("char %c(%d)\n", c,c);
if (c >= font->index1_first && c <= font->index1_last) {
bitoffset = c - font->index1_first;
bitoffset *= font->bits_index;
} else if (c >= font->index2_first && c <= font->index2_last) {
bitoffset = c - font->index2_first + font->index1_last - font->index1_first + 1;
bitoffset *= font->bits_index;
} else if (font->unicode) {
continue;
} else {
continue;
}
//Serial.printf(" index = %d\n", fetchbits_unsigned(font->index, bitoffset, font->bits_index));
data = font->data + fetchbits_unsigned(font->index, bitoffset, font->bits_index);
uint32_t encoding = fetchbits_unsigned(data, 0, 3);
if (encoding != 0) continue;
// uint32_t width = fetchbits_unsigned(data, 3, font->bits_width);
// Serial.printf(" width = %d\n", width);
bitoffset = font->bits_width + 3;
bitoffset += font->bits_height;
// int32_t xoffset = fetchbits_signed(data, bitoffset, font->bits_xoffset);
// Serial.printf(" xoffset = %d\n", xoffset);
bitoffset += font->bits_xoffset;
bitoffset += font->bits_yoffset;
uint32_t delta = fetchbits_unsigned(data, bitoffset, font->bits_delta);
bitoffset += font->bits_delta;
// Serial.printf(" delta = %d\n", delta);
len += delta;//+width-xoffset;
// Serial.printf(" len = %d\n", len);
if ( len > maxlen )
{
maxlen=len;
// Serial.printf(" maxlen = %d\n", maxlen);
}
}
}
str++;
}
// Serial.printf("Return maxlen = %d\n", maxlen);
return( maxlen );
}
void RA8875::charBounds(char c, int16_t *x, int16_t *y,
int16_t *minx, int16_t *miny, int16_t *maxx, int16_t *maxy) {
uint8_t offset=0, _x, _y;
int charW = 0;
// BUGBUG:: Not handling offset/clip
if (font) {
if(c == '\n') { // Newline?
*x = 0; // Reset x to zero, advance y by one line
*y += font->line_space;
} else if(c != '\r') { // Not a carriage return; is normal char
uint32_t bitoffset;
const uint8_t *data;
if (c >= font->index1_first && c <= font->index1_last) {
bitoffset = c - font->index1_first;
bitoffset *= font->bits_index;
} else if (c >= font->index2_first && c <= font->index2_last) {
bitoffset = c - font->index2_first + font->index1_last - font->index1_first + 1;
bitoffset *= font->bits_index;
} else if (font->unicode) {
return; // TODO: implement sparse unicode
} else {
return;
}
//Serial.printf(" index = %d\n", fetchbits_unsigned(font->index, bitoffset, font->bits_index));
data = font->data + fetchbits_unsigned(font->index, bitoffset, font->bits_index);
uint32_t encoding = fetchbits_unsigned(data, 0, 3);
if (encoding != 0) return;
uint32_t width = fetchbits_unsigned(data, 3, font->bits_width);
bitoffset = font->bits_width + 3;
uint32_t height = fetchbits_unsigned(data, bitoffset, font->bits_height);
bitoffset += font->bits_height;
//Serial.printf(" size = %d,%d\n", width, height);
//Serial.printf(" line space = %d\n", font->line_space);
int32_t xoffset = fetchbits_signed(data, bitoffset, font->bits_xoffset);
bitoffset += font->bits_xoffset;
int32_t yoffset = fetchbits_signed(data, bitoffset, font->bits_yoffset);
bitoffset += font->bits_yoffset;
uint32_t delta = fetchbits_unsigned(data, bitoffset, font->bits_delta);
bitoffset += font->bits_delta;
int16_t
x1 = *x + xoffset,
y1 = *y + yoffset,
x2 = x1 + width,
y2 = y1 + height;
if(wrap && (x2 > _width)) {
*x = 0; // Reset x to zero, advance y by one line
*y += font->line_space;
x1 = *x + xoffset,
y1 = *y + yoffset,
x2 = x1 + width,
y2 = y1 + height;
}
if(x1 < *minx) *minx = x1;
if(y1 < *miny) *miny = y1;
if(x2 > *maxx) *maxx = x2;
if(y2 > *maxy) *maxy = y2;
*x += delta; // ? guessing here...
}
}
else if(gfxFont) {
if(c == '\n') { // Newline?
*x = 0; // Reset x to zero, advance y by one line
*y += textsize_y * gfxFont->yAdvance;
} else if(c != '\r') { // Not a carriage return; is normal char
uint8_t first = gfxFont->first,
last = gfxFont->last;
if((c >= first) && (c <= last)) { // Char present in this font?
GFXglyph *glyph = gfxFont->glyph + (c - first);
uint8_t gw = glyph->width,
gh = glyph->height,
xa = glyph->xAdvance;
int8_t xo = glyph->xOffset,
yo = glyph->yOffset + gfxFont->yAdvance/2;
if(wrap && ((*x+(((int16_t)xo+gw)*textsize_x)) > _width)) {
*x = 0; // Reset x to zero, advance y by one line
*y += textsize_y * gfxFont->yAdvance;
}
int16_t tsx = (int16_t)textsize_x,
tsy = (int16_t)textsize_y,
x1 = *x + xo * tsx,
y1 = *y + yo * tsy,
x2 = x1 + gw * tsx - 1,
y2 = y1 + gh * tsy - 1;
if(x1 < *minx) *minx = x1;
if(y1 < *miny) *miny = y1;
if(x2 > *maxx) *maxx = x2;
if(y2 > *maxy) *maxy = y2;
*x += xa * tsx;
}
}
} else { // font test int, tfont default
if(_use_tfont == 1){
if (bitRead(_TXTparameters,0) == 1) offset = 3 * _scaleY;
_x = 0; _y = 0;
if (c == 13){//------------------------------- CARRIAGE ----------------------------------
//ignore
} else if (c == 10){//------------------------- NEW LINE ---------------------------------
_y += (_FNTheight * _scaleY) + _FNTinterline + offset;
} else if (c == 32){//--------------------------- SPACE ---------------------------------
_x += (_spaceCharWidth * _scaleX) + _FNTspacing;
}
else {//-------------------------------------- CHAR ------------------------------------
int charIndex = _getCharCode(c);//get char code
if (charIndex > -1){//valid?
charW = 0;
//get charW and glyph
#if defined(_FORCE_PROGMEM__)
#if defined(ESP8266)
charW = FPSTR(&_currentFont->chars[charIndex].image->image_width);
#if !defined(_RA8875_TXTRNDOPTIMIZER)
const uint8_t * charGlyp = FPSTR(&_currentFont->chars[charIndex].image->data);
#endif
#else
charW = PROGMEM_read(&_currentFont->chars[charIndex].image->image_width);
#if !defined(_RA8875_TXTRNDOPTIMIZER)
const uint8_t * charGlyp = PROGMEM_read(&_currentFont->chars[charIndex].image->data);
#endif
#endif
#else
charW = _currentFont->chars[charIndex].image->image_width;
#if !defined(_RA8875_TXTRNDOPTIMIZER)
const uint8_t * charGlyp = _currentFont->chars[charIndex].image->data;
#endif
#endif
}
}
*x += _x + charW * _scaleX;
*y = _y + (_FNTheight) * _scaleY;
int x2 = *x + charW * _scaleX - 1, // Lower-right pixel of char
y2 = *y + (_FNTheight+3) * _scaleY - 1;
if(x2 > *maxx) *maxx = x2; // Track max x, y
if(y2 > *maxy) *maxy = y2;
if(*x < *minx) *minx = *x; // Track min x, y
if(*y < *miny) *miny = *y;
} else if(_use_int_font == 1){
if(c == '\n') { // Newline?
*x = 0; // Reset x to zero,
*y += _FNTheight * _scaleY; // advance y one line
// min/max x/y unchaged -- that waits for next 'normal' character
} else if(c != '\r') { // Normal char; ignore carriage returns
if(wrap && ((*x + _FNTheight * _scaleY) > _width)) { // Off right?
*x = 0; // Reset x to zero,
*y += _FNTheight * _scaleY; // advance y one line
}
int x2 = *x + _FNTwidth * _scaleX - 1, // Lower-right pixel of char
y2 = *y + _FNTheight * _scaleY - 1;
if(x2 > *maxx) *maxx = x2; // Track max x, y
if(y2 > *maxy) *maxy = y2;
if(*x < *minx) *minx = *x; // Track min x, y
if(*y < *miny) *miny = *y;
*x += _FNTwidth * _scaleX ; // Advance x one char
}
} else {
if(c == '\n') { // Newline?
*x = 0; // Reset x to zero,
*y += textsize_y * 8; // advance y one line
// min/max x/y unchaged -- that waits for next 'normal' character
} else if(c != '\r') { // Normal char; ignore carriage returns
if(wrap && ((*x + textsize_x * 6) > _width)) { // Off right?
*x = 0; // Reset x to zero,
*y += textsize_y * 8; // advance y one line
}
int x2 = *x + textsize_x * 6 - 1, // Lower-right pixel of char
y2 = *y + textsize_y * 8 - 1;
if(x2 > *maxx) *maxx = x2; // Track max x, y
if(y2 > *maxy) *maxy = y2;
if(*x < *minx) *minx = *x; // Track min x, y
if(*y < *miny) *miny = *y;
*x += textsize_x * 6; // Advance x one char
}
}
}
}
// Add in Adafruit versions of text bounds calculations.
void RA8875::getTextBounds(const uint8_t *buffer, uint16_t len, int16_t x, int16_t y,
int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h) {
*x1 = x;
*y1 = y;
*w = *h = 0;
int16_t minx = _width, miny = _height, maxx = -1, maxy = -1;
while(len--)
charBounds(*buffer++, &x, &y, &minx, &miny, &maxx, &maxy);
if(maxx >= minx) {
*x1 = minx;
*w = maxx - minx + 1;
}
if(maxy >= miny) {
*y1 = miny;
*h = maxy - miny + 1;
}
}
void RA8875::getTextBounds(const char *str, int16_t x, int16_t y,
int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h) {
uint8_t c; // Current character
*x1 = x;
*y1 = y;
*w = *h = 0;
int16_t minx = _width, miny = _height, maxx = -1, maxy = -1;
while((c = *str++))
charBounds(c, &x, &y, &minx, &miny, &maxx, &maxy);
if(maxx >= minx) {
*x1 = minx;
*w = maxx - minx + 1;
}
if(maxy >= miny) {
*y1 = miny;
*h = maxy - miny + 1;
}
}
void RA8875::getTextBounds(const String &str, int16_t x, int16_t y,
int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h) {
if (str.length() != 0) {
getTextBounds(const_cast(str.c_str()), x, y, x1, y1, w, h);
}
}
void RA8875::drawFontBits(bool opaque, uint32_t bits, uint32_t numbits, int32_t x, int32_t y, uint32_t repeat)
{
//Serial.printf(" drawFontBits: %d %x %x (%d %d) %u\n", opaque, bits, numbits, x, y, repeat);
if (bits == 0) {
if (opaque) {
fillRect(x, y, numbits, repeat, _TXTBackColor);
}
} else {
int32_t x1 = x;
uint32_t n = numbits;
int w;
int bgw;
w = 0;
bgw = 0;
do {
n--;
if (bits & (1 << n)) {
if (bgw>0) {
if (opaque) {
fillRect(x1 - bgw, y, bgw, repeat, _TXTBackColor);
//Serial.printf(" BG fillrect: %d %d %d %d %x\n", x1 - bgw, y, bgw, repeat, _TXTBackColor);
}
bgw=0;
}
w++;
} else {
if (w>0) {
fillRect(x1 - w, y, w, repeat, _TXTForeColor);
//Serial.printf(" FG fillrect: %d %d %d %d %x\n", x1 - w, y, w, repeat, _TXTForeColor);
w = 0;
}
bgw++;
}
x1++;
} while (n > 0);
if (w > 0) {
fillRect(x1 - w, y, w, repeat, _TXTForeColor);
//Serial.printf(" FG fillrect: %d %d %d %d %x\n", x1 - w, y, w, repeat, _TXTForeColor);
}
if (bgw > 0) {
if (opaque) {
fillRect(x1 - bgw, y, bgw, repeat, _TXTBackColor);
//Serial.printf(" BG fillrect: %d %d %d %d %x\n", x1 - bgw, y, bgw, repeat, _TXTBackColor);
}
}
}
}
void RA8875::drawGFXFontChar(unsigned int c) {
// Lets do Adafruit GFX character output here as well
if(c == '\r') return;
// Some quick and dirty tests to see if we can
uint8_t first = gfxFont->first;
if((c < first) || (c > gfxFont->last)) return;
GFXglyph *glyph = gfxFont->glyph + (c - first);
uint8_t w = glyph->width,
h = glyph->height;
//Serial.printf("w = %d, h = %d\n", w, h);
//if((w == 0) || (h == 0)) return; // Is there an associated bitmap?
int16_t xo = glyph->xOffset; // sic
int16_t yo = glyph->yOffset + gfxFont->yAdvance/2;
if(wrap && ((_cursorX + textsize_x * (xo + w)) > _width)) {
_cursorX = 0;
_cursorY += (int16_t)textsize_y * gfxFont->yAdvance;
}
// Lets do the work to output the font character
uint8_t *bitmap = gfxFont->bitmap;
uint16_t bo = glyph->bitmapOffset;
uint8_t xx, yy, bits = 0, bit = 0;
//Serial.printf("DGFX_char: %c (%d,%d) : %u %u %u %u %d %d %x %x %d\n", c, _cursorX, _cursorY, w, h,
// glyph->xAdvance, gfxFont->yAdvance, xo, yo, _TXTForeColor, _TXTBackColor, 0); Serial.flush();
if (_backTransparent) {
//Serial.printf("DGFXChar: %c %u, %u, wh:%d %d o:%d %d\n", c, _cursorX, _cursorY, w, h, xo, yo);
// Todo: Add character clipping here
// NOTE: Adafruit GFX does not support Opaque font output as there
// are issues with proportionally spaced characters that may overlap
// So the below is not perfect as we may overwrite a small portion
// of a letter with the next one, when we blank out...
// But: I prefer to let each of us decide if the limitations are
// worth it or not. If Not you still have the option to not
// Do transparent mode and instead blank out and blink...
for(yy=0; yy= 8) && ((bits & 0xff) == 0xff)) {
xCount = 8;
//Serial.print("8");
fillRect(_cursorX+(xo+xx)*textsize_x, _cursorY+(yo+yy)*textsize_y,
xCount * textsize_x, textsize_y, _TXTForeColor);
} else if ((w_left >= 4) && ((bits & 0xf0) == 0xf0)) {
xCount = 4;
//Serial.print("4");
fillRect(_cursorX+(xo+xx)*textsize_x, _cursorY+(yo+yy)*textsize_y,
xCount * textsize_x, textsize_y, _TXTForeColor);
} else if ((w_left >= 3) && ((bits & 0xe0) == 0xe0)) {
//Serial.print("3");
xCount = 3;
fillRect(_cursorX+(xo+xx)*textsize_x, _cursorY+(yo+yy)*textsize_y,
xCount * textsize_x, textsize_y, _TXTForeColor);
} else if ((w_left >= 2) && ((bits & 0xc0) == 0xc0)) {
//Serial.print("2");
xCount = 2;
fillRect(_cursorX+(xo+xx)*textsize_x, _cursorY+(yo+yy)*textsize_y,
xCount * textsize_x, textsize_y, _TXTForeColor);
} else {
xCount = 1;
if(bits & 0x80) {
if((textsize_x == 1) && (textsize_y == 1)){
drawPixel(_cursorX+xo+xx, _cursorY+yo+yy, _TXTForeColor);
} else {
fillRect(_cursorX+(xo+xx)*textsize_x, _cursorY+(yo+yy)*textsize_y,textsize_x, textsize_y, _TXTForeColor);
}
}
}
xx += xCount;
w_left -= xCount;
bit += xCount;
bits <<= xCount;
}
}
_gfx_last_char_x_write = 0;
} else {
// To Do, properly clipping and offsetting...
// This solid background approach is about 5 time faster
// Lets calculate bounding rectangle that we will update
// We need to offset by the origin.
// We are going direct so do some offsets and clipping
int16_t x_offset_cursor = _cursorX + _originx; // This is where the offseted cursor is.
int16_t x_start = x_offset_cursor; // I am assuming no negative x offsets.
int16_t x_end = x_offset_cursor + (glyph->xAdvance * textsize_x);
if (glyph->xAdvance < (xo + w)) x_end = x_offset_cursor + ((xo + w)* textsize_x); // BUGBUG Overlflows into next char position.
int16_t x_left_fill = x_offset_cursor + xo * textsize_x;
int16_t x;
if (xo < 0) {
// Unusual character that goes back into previous character
//Serial.printf("GFX Font char XO < 0: %c %d %d %u %u %u\n", c, xo, yo, w, h, glyph->xAdvance );
x_start += xo * textsize_x;
x_left_fill = 0; // Don't need to fill anything here...
}
int16_t y_start = _cursorY + _originy + (_gfxFont_min_yOffset * textsize_y)+ gfxFont->yAdvance*textsize_y/2; // UP to most negative value.
int16_t y_end = y_start + gfxFont->yAdvance * textsize_y; // how far we will update
int16_t y = y_start;
//int8_t y_top_fill = (yo - _gfxFont_min_yOffset) * textsize_y; // both negative like -10 - -16 = 6...
int8_t y_top_fill = (yo - gfxFont->yAdvance/2 - _gfxFont_min_yOffset) * textsize_y;
// See if anything is within clip rectangle, if not bail
if((x_start >= _displayclipx2) || // Clip right
(y_start >= _displayclipy2) || // Clip bottom
(x_end < _displayclipx1) || // Clip left
(y_end < _displayclipy1)) // Clip top
{
// But remember to first update the cursor position
_cursorX += glyph->xAdvance * (int16_t)textsize_x;
return;
}
// If our y_end > _displayclipy2 set it to _displayclipy2 as to not have to test both Likewise X
if (y_end > _displayclipy2) y_end = _displayclipy2;
if (x_end > _displayclipx2) x_end = _displayclipx2;
// If we get here and
if (_gfx_last__cursorY != (_cursorY + _originy)) _gfx_last_char_x_write = 0;
// lets try to output text in one output rectangle
//Serial.printf(" SPI (%d %d) (%d %d)\n", x_start, y_start, x_end, y_end);Serial.flush();
// compute the actual region we will output given
_startSend();
//setActiveWindow((x_start >= _displayclipx1) ? x_start : _displayclipx1,
// (y_start >= _displayclipy1) ? y_start : _displayclipy1,
// x_end - 1, y_end - 1);
//writeCommand(RA8875_MRWC);
//Serial.printf("SetAddr: %u %u %u %u\n", (x_start >= _displayclipx1) ? x_start : _displayclipx1,
// (y_start >= _displayclipy1) ? y_start : _displayclipy1,
// x_end - 1, y_end - 1);
// First lets fill in the top parts above the actual rectangle...
//Serial.printf(" y_top_fill %d x_left_fill %d\n", y_top_fill, x_left_fill);
while (y_top_fill--) {
if ( (y >= _displayclipy1) && (y < _displayclipy2)) {
for (int16_t xx = x_start; xx < x_end; xx++) {
if (xx >= _displayclipx1) {
combineAndDrawPixel(xx, y, gfxFontLastCharPosFG(xx,y)? _gfx_last_char_textcolor : (xx < x_offset_cursor)? _gfx_last_char_textbgcolor : _TXTBackColor);
}
}
}
forceCombinedPixelsOut();
y++;
}
//Serial.println(" After top fill"); Serial.flush();
// Now lets output all of the pixels for each of the rows..
for(yy=0; (yy= _displayclipy1) {
while (x < x_left_fill) {
if ( (x >= _displayclipx1) && (x < _displayclipx2)) {
// Don't need to check if we are in previous char as in this case x_left_fill is set to 0...
combineAndDrawPixel(x, y, gfxFontLastCharPosFG(x,y)? _gfx_last_char_textcolor : _TXTBackColor);
}
x++;
}
for(xx=0; xx= _displayclipx1) && (x < _displayclipx2)) {
if (bits & 0x80)
combineAndDrawPixel(x, y, _TXTForeColor);
else
combineAndDrawPixel(x, y,gfxFontLastCharPosFG(x,y)? _gfx_last_char_textcolor : (x < x_offset_cursor)? _gfx_last_char_textbgcolor : _TXTBackColor);
}
x++; // remember our logical position...
}
bits <<= 1;
}
// Fill in any additional bg colors to right of our output
while (x < x_end) {
if (x >= _displayclipx1) {
combineAndDrawPixel(x, y, gfxFontLastCharPosFG(x,y)? _gfx_last_char_textcolor : (x < x_offset_cursor)? _gfx_last_char_textbgcolor : _TXTBackColor);
}
x++;
}
}
forceCombinedPixelsOut();
y++; // remember which row we just output
}
}
// And output any more rows below us...
//Serial.println(" Bottom fill"); Serial.flush();
while (y < y_end) {
if (y >= _displayclipy1) {
for (int16_t xx = x_start; xx < x_end; xx++) {
if (xx >= _displayclipx1) {
combineAndDrawPixel(xx ,y, gfxFontLastCharPosFG(xx,y)? _gfx_last_char_textcolor : (xx < x_offset_cursor)? _gfx_last_char_textbgcolor : _TXTBackColor);
}
}
}
forceCombinedPixelsOut();
y++;
}
//writecommand_last(ILI9488_NOP);
//_endSend();
//setActiveWindow();
_gfx_c_last = c;
_gfx_last__cursorX = _cursorX + _originx;
_gfx_last__cursorY = _cursorY + _originy;
_gfx_last_char_x_write = x_end;
_gfx_last_char_textcolor = _TXTForeColor;
_gfx_last_char_textbgcolor = _TXTBackColor;
}
_cursorX += glyph->xAdvance * (int16_t)textsize_x;
}
// Some fonts overlap characters if we detect that the previous
// character wrote out more width than they advanced in X direction
// we may want to know if the last character output a FG or BG at a position.
// Opaque font chracter overlap?
// unsigned int _gfx_c_last;
// int16_t _gfx_last__cursorX, _gfx_last__cursorY;
// int16_t _gfx_last_x_overlap = 0;
bool RA8875::gfxFontLastCharPosFG(int16_t x, int16_t y) {
GFXglyph *glyph = gfxFont->glyph + (_gfx_c_last - gfxFont->first);
uint8_t w = glyph->width,
h = glyph->height;
int16_t xo = glyph->xOffset; // sic
int16_t yo = glyph->yOffset + gfxFont->yAdvance/2;
if (x >= _gfx_last_char_x_write) return false; // we did not update here...
if (y < (_gfx_last__cursorY + (yo*textsize_y))) return false; // above
if (y >= (_gfx_last__cursorY + (yo+h)*textsize_y)) return false; // below
// Lets compute which Row this y is in the bitmap
int16_t y_bitmap = (y - ((_gfx_last__cursorY + (yo*textsize_y))) + textsize_y - 1) / textsize_y;
int16_t x_bitmap = (x - ((_gfx_last__cursorX + (xo*textsize_x))) + textsize_x - 1) / textsize_x;
uint16_t pixel_bit_offset = y_bitmap * w + x_bitmap;
return ((gfxFont->bitmap[glyph->bitmapOffset + (pixel_bit_offset >> 3)]) & (0x80 >> (pixel_bit_offset & 0x7)));
}
void RA8875::setTextSize(uint8_t s_x, uint8_t s_y) {
textsize_x = (s_x > 0) ? s_x : 1;
textsize_y = (s_y > 0) ? s_y : 1;
_gfx_last_char_x_write = 0; // Don't use cached data here
}
void RA8875::drawFontPixel( uint8_t alpha, uint32_t x, uint32_t y ){
// Adjust alpha based on the number of alpha levels supported by the font (based on bpp)
// Note: Implemented look-up table for alpha, but made absolutely no difference in speed (T3.6)
alpha = (uint8_t)(alpha * fontalphamx);
uint32_t result = ((((textcolorPrexpanded - textbgcolorPrexpanded) * alpha) >> 5) + textbgcolorPrexpanded) & 0b00000111111000001111100000011111;
Pixel(x,y,(uint16_t)((result >> 16) | result));
}
void RA8875::Pixel(int16_t x, int16_t y, uint16_t color)
{
x+=_originx;
y+=_originy;
if((x < _displayclipx1) ||(x >= _displayclipx2) || (y < _displayclipy1) || (y >= _displayclipy2)) return;
setActiveWindow(x, y, x, y);
writeCommand(RA8875_MRWC);
drawPixel(x, y, color);
}
uint32_t RA8875::fetchbit(const uint8_t *p, uint32_t index)
{
if (p[index >> 3] & (1 << (7 - (index & 7)))) return 1;
return 0;
}
uint32_t RA8875::fetchbits_unsigned(const uint8_t *p, uint32_t index, uint32_t required)
{
uint32_t val = 0;
do {
uint8_t b = p[index >> 3];
uint32_t avail = 8 - (index & 7);
if (avail <= required) {
val <<= avail;
val |= b & ((1 << avail) - 1);
index += avail;
required -= avail;
} else {
b >>= avail - required;
val <<= required;
val |= b & ((1 << required) - 1);
break;
}
} while (required);
return val;
}
uint32_t RA8875::fetchbits_signed(const uint8_t *p, uint32_t index, uint32_t required)
{
uint32_t val = fetchbits_unsigned(p, index, required);
if (val & (1 << (required - 1))) {
return (int32_t)val - (1 << required);
}
return (int32_t)val;
}
uint32_t RA8875::fetchpixel(const uint8_t *p, uint32_t index, uint32_t x)
{
// The byte
uint8_t b = p[index >> 3];
// Shift to LSB position and mask to get value
uint8_t s = ((fontppb-(x % fontppb)-1)*fontbpp);
// Mask and return
return (b >> s) & fontbppmask;
}
void RA8875::write16BitColor(uint16_t color, bool last_pixel){
writeData16(color);
}
/*
void RA8875::debugData(uint16_t data,uint8_t len)
{
int i;
for (i=len-1; i>=0; i--){
if (bitRead(data,i)==1){
Serial.print("1");
} else {
Serial.print("0");
}
}
Serial.print(" -> 0x");
Serial.print(data,HEX);
Serial.print("\n");
}
*/
/*
void RA8875::showLineBuffer(uint8_t data[],int len)
{
int i;
for (i=0; i