diff --git a/README.md b/README.md index 8d803f1..e6fd97a 100644 --- a/README.md +++ b/README.md @@ -301,6 +301,9 @@ Work is in progress... --- ## Version history +#### v0.8.100 +- added support for GC9A01A display https://aliexpress.com/item/1005004069703494.html?sku_id=12000029869654615 + #### v0.8.089 - increased length of SSID string to 30 characters (requires full update + ESP32 Data Upload) - fixed artifacts when adjusting the volume on OLED displays diff --git a/yoRadio/src/Adafruit_GC9A01A/Adafruit_GC9A01A.cpp b/yoRadio/src/Adafruit_GC9A01A/Adafruit_GC9A01A.cpp new file mode 100644 index 0000000..4cd2053 --- /dev/null +++ b/yoRadio/src/Adafruit_GC9A01A/Adafruit_GC9A01A.cpp @@ -0,0 +1,289 @@ +/*! + * @file Adafruit_GC9A01A.cpp + * + * @mainpage GC9A01A TFT Display library for Adafruit_GFX + * + * @section intro_sec Introduction + * + * Library to provide GC9A01A display driver support in Adafruit_GFX. + * ADAFRUIT DOES NOT PROVIDE TECHNICAL SUPPORT FOR THESE DISPLAYS NOR + * THIS CODE. This was adapted from a prior library (Adafruit_ILI9341) + * for use with an IPS screen from BuyDisplay.com. + * + * These displays use SPI to communicate, 4 or 5 pins are required + * to interface (RST is optional). + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * @section dependencies Dependencies + * + * This library depends on + * Adafruit_GFX being present on your system. Please make sure you have + * installed the latest version before using this library. + * + * @section author Author + * + * Written by Limor "ladyada" Fried for Adafruit Industries. + * GC9A01A adaptation by Phil "PaintYourDragon" Burgess. + * + * @section license License + * + * BSD license, all text here must be included in any redistribution. + * + */ +#include "../core/options.h" +#if DSP_MODEL==DSP_GC9A01A + +#include "Adafruit_GC9A01A.h" +#ifndef ARDUINO_STM32_FEATHER +#include "pins_arduino.h" +#ifndef RASPI +#include "wiring_private.h" +#endif +#endif +#include + +#if defined(ARDUINO_ARCH_ARC32) || defined(ARDUINO_MAXIM) +#define SPI_DEFAULT_FREQ 16000000 +// Teensy 3.0, 3.1/3.2, 3.5, 3.6 +#elif defined(__MK20DX128__) || defined(__MK20DX256__) || \ + defined(__MK64FX512__) || defined(__MK66FX1M0__) +#define SPI_DEFAULT_FREQ 40000000 +#elif defined(__AVR__) || defined(TEENSYDUINO) +#define SPI_DEFAULT_FREQ 8000000 +#elif defined(ESP8266) || defined(ESP32) +#define SPI_DEFAULT_FREQ 40000000 +#elif defined(RASPI) +#define SPI_DEFAULT_FREQ 80000000 +#elif defined(ARDUINO_ARCH_STM32F1) +#define SPI_DEFAULT_FREQ 36000000 +#else +#define SPI_DEFAULT_FREQ 24000000 ///< Default SPI data clock frequency +#endif + +#define MADCTL_MY 0x80 ///< Bottom to top +#define MADCTL_MX 0x40 ///< Right to left +#define MADCTL_MV 0x20 ///< Reverse Mode +#define MADCTL_ML 0x10 ///< LCD refresh Bottom to top +#define MADCTL_RGB 0x00 ///< Red-Green-Blue pixel order +#define MADCTL_BGR 0x08 ///< Blue-Green-Red pixel order +#define MADCTL_MH 0x04 ///< LCD refresh right to left + +/*! + @brief Instantiate Adafruit GC9A01A driver with software SPI + @param cs Chip select pin # + @param dc Data/Command pin # + @param mosi SPI MOSI pin # + @param sclk SPI Clock pin # + @param rst Reset pin # (optional, pass -1 if unused) + @param miso SPI MISO pin # (optional, pass -1 if unused) +*/ +Adafruit_GC9A01A::Adafruit_GC9A01A(int8_t cs, int8_t dc, int8_t mosi, + int8_t sclk, int8_t rst, int8_t miso) + : Adafruit_SPITFT(GC9A01A_TFTWIDTH, GC9A01A_TFTHEIGHT, cs, dc, mosi, sclk, + rst, miso) {} + +/*! + @brief Instantiate Adafruit GC9A01A driver with hardware SPI using the + default SPI peripheral. + @param cs Chip select pin # (OK to pass -1 if CS tied to GND). + @param dc Data/Command pin # (required). + @param rst Reset pin # (optional, pass -1 if unused). +*/ +Adafruit_GC9A01A::Adafruit_GC9A01A(int8_t cs, int8_t dc, int8_t rst) + : Adafruit_SPITFT(GC9A01A_TFTWIDTH, GC9A01A_TFTHEIGHT, cs, dc, rst) {} + +#if !defined(ESP8266) +/*! + @brief Instantiate Adafruit GC9A01A driver with hardware SPI using + a specific SPI peripheral (not necessarily default). + @param spiClass Pointer to SPI peripheral (e.g. &SPI or &SPI1). + @param dc Data/Command pin # (required). + @param cs Chip select pin # (optional, pass -1 if unused and + CS is tied to GND). + @param rst Reset pin # (optional, pass -1 if unused). +*/ +Adafruit_GC9A01A::Adafruit_GC9A01A(SPIClass *spiClass, int8_t dc, int8_t cs, + int8_t rst) + : Adafruit_SPITFT(GC9A01A_TFTWIDTH, GC9A01A_TFTHEIGHT, spiClass, cs, dc, + rst) {} +#endif // end !ESP8266 + +/*! + @brief Instantiate Adafruit GC9A01A driver using parallel interface. + @param busWidth If tft16 (enumeration in Adafruit_SPITFT.h), is a + 16-bit interface, else 8-bit. + @param d0 Data pin 0 (MUST be a byte- or word-aligned LSB of a + PORT register -- pins 1-n are extrapolated from this). + @param wr Write strobe pin # (required). + @param dc Data/Command pin # (required). + @param cs Chip select pin # (optional, pass -1 if unused and CS + is tied to GND). + @param rst Reset pin # (optional, pass -1 if unused). + @param rd Read strobe pin # (optional, pass -1 if unused). +*/ +Adafruit_GC9A01A::Adafruit_GC9A01A(tftBusWidth busWidth, int8_t d0, int8_t wr, + int8_t dc, int8_t cs, int8_t rst, int8_t rd) + : Adafruit_SPITFT(GC9A01A_TFTWIDTH, GC9A01A_TFTHEIGHT, busWidth, d0, wr, dc, + cs, rst, rd) {} + +// clang-format off +static const uint8_t PROGMEM initcmd[] = { + GC9A01A_INREGEN2, 0, + 0xEB, 1, 0x14, + GC9A01A_INREGEN1, 0, + GC9A01A_INREGEN2, 0, + 0xEB, 1, 0x14, + 0x84, 1, 0x40, + 0x85, 1, 0xFF, + 0x86, 1, 0xFF, + 0x87, 1, 0xFF, + 0x88, 1, 0x0A, + 0x89, 1, 0x21, + 0x8A, 1, 0x00, + 0x8B, 1, 0x80, + 0x8C, 1, 0x01, + 0x8D, 1, 0x01, + 0x8E, 1, 0xFF, + 0x8F, 1, 0xFF, + 0xB6, 2, 0x00, 0x00, + GC9A01A_MADCTL, 1, MADCTL_MX | MADCTL_BGR, + GC9A01A_PIXFMT, 1, 0x05, + 0x90, 4, 0x08, 0x08, 0x08, 0x08, + 0xBD, 1, 0x06, + 0xBC, 1, 0x00, + 0xFF, 3, 0x60, 0x01, 0x04, + GC9A01A1_VREG1A, 0x13, + GC9A01A1_VREG1B, 0x13, + GC9A01A1_VREG2A, 0x22, + 0xBE, 1, 0x11, + ILI9341_GMCTRN1, 2, 0x10, 0x0E, + 0xDF, 3, 0x21, 0x0c, 0x02, + GC9A01A_GAMMA1, 6, 0x45, 0x09, 0x08, 0x08, 0x26, 0x2A, + GC9A01A_GAMMA2, 6, 0x43, 0x70, 0x72, 0x36, 0x37, 0x6F, + GC9A01A_GAMMA3, 6, 0x45, 0x09, 0x08, 0x08, 0x26, 0x2A, + GC9A01A_GAMMA4, 6, 0x43, 0x70, 0x72, 0x36, 0x37, 0x6F, + 0xED, 2, 0x1B, 0x0B, + 0xAE, 1, 0x77, + 0xCD, 1, 0x63, + 0x70, 9, 0x07, 0x07, 0x04, 0x0E, 0x0F, 0x09, 0x07, 0x08, 0x03, + ILI9341_FRAMERATE, 1, 0x34, + 0x62, 12, 0x18, 0x0D, 0x71, 0xED, 0x70, 0x70, + 0x18, 0x0F, 0x71, 0xEF, 0x70, 0x70, + 0x63, 12, 0x18, 0x11, 0x71, 0xF1, 0x70, 0x70, + 0x18, 0x13, 0x71, 0xF3, 0x70, 0x70, + 0x64, 7, 0x28, 0x29, 0xF1, 0x01, 0xF1, 0x00, 0x07, + 0x66, 10, 0x3C, 0x00, 0xCD, 0x67, 0x45, 0x45, 0x10, 0x00, 0x00, 0x00, + 0x67, 10, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x54, 0x10, 0x32, 0x98, + 0x74, 7, 0x10, 0x85, 0x80, 0x00, 0x00, 0x4E, 0x00, + 0x98, 2, 0x3e, 0x07, + GC9A01A_TEON, 0, + GC9A01A_INVON, 0, + GC9A01A_SLPOUT, 0x80, // Exit sleep + GC9A01A_DISPON, 0x80, // Display on + 0x00 // End of list +}; +// clang-format on + +/*! + @brief Initialize GC9A01A chip. Connects to the GC9A01A over SPI + and sends initialization commands. + @param freq Desired SPI clock frequency +*/ +void Adafruit_GC9A01A::begin(uint32_t freq) { + + if (!freq) + freq = SPI_DEFAULT_FREQ; + initSPI(freq); + + if (_rst < 0) { // If no hardware reset pin... + sendCommand(GC9A01A_SWRESET); // Engage software reset + delay(150); + } + + uint8_t cmd, x, numArgs; + const uint8_t *addr = initcmd; + while ((cmd = pgm_read_byte(addr++)) > 0) { + x = pgm_read_byte(addr++); + numArgs = x & 0x7F; + sendCommand(cmd, addr, numArgs); + addr += numArgs; + if (x & 0x80) + delay(150); + } + + _width = GC9A01A_TFTWIDTH; + _height = GC9A01A_TFTHEIGHT; +} + +/*! + @brief Set origin of (0,0) and orientation of TFT display + @param m The index for rotation, from 0-3 inclusive +*/ +void Adafruit_GC9A01A::setRotation(uint8_t m) { + rotation = m % 4; // can't be higher than 3 + switch (rotation) + { + case 0: + m = (MADCTL_MX | MADCTL_BGR); + _width = GC9A01A_TFTWIDTH; + _height = GC9A01A_TFTHEIGHT; + break; + case 1: + m = (MADCTL_MV | MADCTL_BGR); + _width = GC9A01A_TFTHEIGHT; + _height = GC9A01A_TFTWIDTH; + break; + case 2: + m = (MADCTL_MY | MADCTL_BGR); + _width = GC9A01A_TFTWIDTH; + _height = GC9A01A_TFTHEIGHT; + break; + case 3: + m = (MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_BGR); + _width = GC9A01A_TFTHEIGHT; + _height = GC9A01A_TFTWIDTH; + break; + } + sendCommand(GC9A01A_MADCTL, &m, 1); +} + +/*! + @brief Enable/Disable display color inversion + @param invert True to invert, False to have normal color +*/ +void Adafruit_GC9A01A::invertDisplay(bool invert) { + sendCommand(invert ? GC9A01A_INVON : GC9A01A_INVOFF); +} + +/*! + @brief Set the "address window" - the rectangle we will write to RAM + with the next chunk of SPI data. The GC9A01A will automatically + wrap the data as each row is filled. + @param x1 TFT memory 'x' origin + @param y1 TFT memory 'y' origin + @param w Width of rectangle + @param h Height of rectangle +*/ +void Adafruit_GC9A01A::setAddrWindow(uint16_t x1, uint16_t y1, uint16_t w, + uint16_t h) { + uint16_t x2 = (x1 + w - 1), y2 = (y1 + h - 1); + writeCommand(GC9A01A_CASET); // Column address set + SPI_WRITE16(x1); + SPI_WRITE16(x2); + writeCommand(GC9A01A_PASET); // Row address set + SPI_WRITE16(y1); + SPI_WRITE16(y2); + writeCommand(GC9A01A_RAMWR); // Write to RAM +} + +void Adafruit_GC9A01A::enableDisplay(boolean enable) { + sendCommand(enable ? GC9A01A_DISPON : GC9A01A_DISPOFF); +} + +void Adafruit_GC9A01A::enableSleep(boolean enable) { + sendCommand(enable ? GC9A01A_SLPIN : GC9A01A_SLPOUT); +} +#endif diff --git a/yoRadio/src/Adafruit_GC9A01A/Adafruit_GC9A01A.h b/yoRadio/src/Adafruit_GC9A01A/Adafruit_GC9A01A.h new file mode 100644 index 0000000..296aa12 --- /dev/null +++ b/yoRadio/src/Adafruit_GC9A01A/Adafruit_GC9A01A.h @@ -0,0 +1,170 @@ +/*! + * @file Adafruit_GC9A01A.h + * + * Library to provide GC9A01A display driver support in Adafruit_GFX. + * ADAFRUIT DOES NOT PROVIDE TECHNICAL SUPPORT FOR THESE DISPLAYS NOR + * THIS CODE. This was adapted from a prior library (Adafruit_ILI9341) + * for use with an IPS screen from BuyDisplay.com. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * This library depends on + * Adafruit_GFX being present on your system. Please make sure you have + * installed the latest version before using this library. + * + * Written by Limor "ladyada" Fried for Adafruit Industries. + * GC9A01A adaptation by Phil "PaintYourDragon" Burgess. + * + * BSD license, all text here must be included in any redistribution. + * + */ + +#pragma once + +#include "Adafruit_GFX.h" +#include "Arduino.h" +#include "Print.h" +#include +#include +#include + +#define GC9A01A_TFTWIDTH 240 ///< Display width in pixels +#define GC9A01A_TFTHEIGHT 240 ///< Display height in pixels + +// NOTE: ILI9341 registers defined (but commented out) are ones that +// *might* be compatible with the GC9A01A, but aren't documented in +// the device datasheet. A couple are defined (with ILI name) and NOT +// commented out because they appeared in the manufacturer example code +// as raw register addresses, no documentation in datasheet, they SEEM +// to do the same thing as their ILI equivalents but this is not 100% +// confirmed so they've not been assigned GC9A01A register defines. + +//#define ILI9341_NOP 0x00 ///< No-op register +#define GC9A01A_SWRESET 0x01 ///< Software reset register + +//#define GC9A01A 0x04 ///< Read display identification information +//#define GC9A01A 0x09 ///< Read Display Status + +#define GC9A01A_SLPIN 0x10 ///< Enter Sleep Mode +#define GC9A01A_SLPOUT 0x11 ///< Sleep Out +#define GC9A01A_PTLON 0x12 ///< Partial Mode ON +#define GC9A01A_NORON 0x13 ///< Normal Display Mode ON + +//#define ILI9341_RDMODE 0x0A ///< Read Display Power Mode +//#define ILI9341_RDMADCTL 0x0B ///< Read Display MADCTL +//#define ILI9341_RDPIXFMT 0x0C ///< Read Display Pixel Format +//#define ILI9341_RDIMGFMT 0x0D ///< Read Display Image Format +//#define ILI9341_RDSELFDIAG 0x0F ///< Read Display Self-Diagnostic Result + +#define GC9A01A_INVOFF 0x20 ///< Display Inversion OFF +#define GC9A01A_INVON 0x21 ///< Display Inversion ON +//#define ILI9341_GAMMASET 0x26 ///< Gamma Set +#define GC9A01A_DISPOFF 0x28 ///< Display OFF +#define GC9A01A_DISPON 0x29 ///< Display ON + +#define GC9A01A_CASET 0x2A ///< Column Address Set +#define GC9A01A_PASET 0x2B ///< Page Address Set +#define GC9A01A_RAMWR 0x2C ///< Memory Write +//#define ILI9341_RAMRD 0x2E ///< Memory Read + +#define GC9A01A_PTLAR 0x30 ///< Partial Area +#define GC9A01A_VSCRDEF 0x33 ///< Vertical Scrolling Definition +#define GC9A01A_TEOFF 0x34 ///< Tearing effect line off +#define GC9A01A_TEON 0x35 ///< Tearing effect line on +#define GC9A01A_MADCTL 0x36 ///< Memory Access Control +#define GC9A01A_VSCRSADD 0x37 ///< Vertical Scrolling Start Address +#define GC9A01A_PIXFMT 0x3A ///< COLMOD: Pixel Format Set +// 0x3C = Write memory continue +// 0x44 = Set tear scanline +// 0x45 = Get scanline +// 0x51 = Write display brightness +// 0x53 = Write CTRL display + +// 0xA7 = Vcore voltage control + +// 0xB0 = RGB interface signal control +//#define ILI9341_FRMCTR1 0xB1 ///< Frame Rate Control (In Normal Mode/Full Colors) +//#define ILI9341_FRMCTR2 0xB2 ///< Frame Rate Control (In Idle Mode/8 colors) +//#define ILI9341_FRMCTR3 0xB3 ///< Frame Rate control (In Partial Mode/Full Colors) +//#define ILI9341_INVCTR 0xB4 ///< Display Inversion Control +// 0xB5 = Blanking porch control +#define GC9A01A1_DFUNCTR 0xB6 ///< Display Function Control +// 0xBA = TE control + +// 0xC1 = Power criterion control +#define GC9A01A1_VREG1A 0xC3 ///< Vreg1a voltage control +#define GC9A01A1_VREG1B 0xC4 ///< Vreg1b voltage control +//#define ILI9341_PWCTR1 0xC0 ///< Power Control 1 +//#define ILI9341_PWCTR2 0xC1 ///< Power Control 2 +//#define ILI9341_PWCTR3 0xC2 ///< Power Control 3 +//#define ILI9341_PWCTR4 0xC3 ///< Power Control 4 +//#define ILI9341_PWCTR5 0xC4 ///< Power Control 5 +//#define ILI9341_VMCTR1 0xC5 ///< VCOM Control 1 +//#define ILI9341_VMCTR2 0xC7 ///< VCOM Control 2 +#define GC9A01A1_VREG2A 0xC9 ///< Vreg2a voltage control + +#define GC9A01A_RDID1 0xDA ///< Read ID 1 +#define GC9A01A_RDID2 0xDB ///< Read ID 2 +#define GC9A01A_RDID3 0xDC ///< Read ID 3 +//#define ILI9341_RDID4 0xDD ///< Read ID 4 + +#define ILI9341_GMCTRP1 0xE0 ///< Positive Gamma Correction +#define ILI9341_GMCTRN1 0xE1 ///< Negative Gamma Correction +#define ILI9341_FRAMERATE 0xE8 ///< Frame rate control +// 0xE9 = SPI 2data control +// 0xEC = Charge pump frequent control +#define GC9A01A_INREGEN2 0xEF ///< Inter register enable 2 +#define GC9A01A_GAMMA1 0xF0 ///< Set gamma 1 +#define GC9A01A_GAMMA2 0xF1 ///< Set gamma 2 +#define GC9A01A_GAMMA3 0xF2 ///< Set gamma 3 +#define GC9A01A_GAMMA4 0xF3 ///< Set gamma 4 +//#define ILI9341_PWCTR6 0xFC +// 0xF6 = Interface control +#define GC9A01A_INREGEN1 0xFE ///< Inter register enable 1 + +// Color definitions +#define GC9A01A_BLACK 0x0000 ///< 0, 0, 0 +#define GC9A01A_NAVY 0x000F ///< 0, 0, 123 +#define GC9A01A_DARKGREEN 0x03E0 ///< 0, 125, 0 +#define GC9A01A_DARKCYAN 0x03EF ///< 0, 125, 123 +#define GC9A01A_MAROON 0x7800 ///< 123, 0, 0 +#define GC9A01A_PURPLE 0x780F ///< 123, 0, 123 +#define GC9A01A_OLIVE 0x7BE0 ///< 123, 125, 0 +#define GC9A01A_LIGHTGREY 0xC618 ///< 198, 195, 198 +#define GC9A01A_DARKGREY 0x7BEF ///< 123, 125, 123 +#define GC9A01A_BLUE 0x001F ///< 0, 0, 255 +#define GC9A01A_GREEN 0x07E0 ///< 0, 255, 0 +#define GC9A01A_CYAN 0x07FF ///< 0, 255, 255 +#define GC9A01A_RED 0xF800 ///< 255, 0, 0 +#define GC9A01A_MAGENTA 0xF81F ///< 255, 0, 255 +#define GC9A01A_YELLOW 0xFFE0 ///< 255, 255, 0 +#define GC9A01A_WHITE 0xFFFF ///< 255, 255, 255 +#define GC9A01A_ORANGE 0xFD20 ///< 255, 165, 0 +#define GC9A01A_GREENYELLOW 0xAFE5 ///< 173, 255, 41 +#define GC9A01A_PINK 0xFC18 ///< 255, 130, 198 + +/*! +@brief Class to manage hardware interface with GC9A01A chipset. +*/ +class Adafruit_GC9A01A : public Adafruit_SPITFT { +public: + Adafruit_GC9A01A(int8_t _CS, int8_t _DC, int8_t _MOSI, int8_t _SCLK, + int8_t _RST = -1, int8_t _MISO = -1); + Adafruit_GC9A01A(int8_t _CS, int8_t _DC, int8_t _RST = -1); +#if !defined(ESP8266) + Adafruit_GC9A01A(SPIClass *spiClass, int8_t dc, int8_t cs = -1, + int8_t rst = -1); +#endif // end !ESP8266 + Adafruit_GC9A01A(tftBusWidth busWidth, int8_t d0, int8_t wr, int8_t dc, + int8_t cs = -1, int8_t rst = -1, int8_t rd = -1); + + void begin(uint32_t freq = 0); + void setRotation(uint8_t r); + void invertDisplay(bool i); + + void setAddrWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h); + void enableDisplay(boolean enable); + void enableSleep(boolean enable); +}; diff --git a/yoRadio/src/core/display.cpp b/yoRadio/src/core/display.cpp index d381b9c..4a3b97d 100644 --- a/yoRadio/src/core/display.cpp +++ b/yoRadio/src/core/display.cpp @@ -245,7 +245,7 @@ void Display::_swichMode(displayMode_e newmode) { #ifdef META_MOVE _meta.moveBack(); #endif - _meta.setAlign(WA_LEFT); + _meta.setAlign(metaConf.widget.align); _meta.setText(config.station.name); _nums.setText(""); _pager.setPage( pages[PG_PLAYER]); @@ -409,7 +409,7 @@ void Display::loop() { } void Display::_station() { - _meta.setAlign(WA_LEFT); + _meta.setAlign(metaConf.widget.align); _meta.setText(config.station.name); /*#ifdef USE_NEXTION nextion.newNameset(config.station.name); diff --git a/yoRadio/src/core/options.h b/yoRadio/src/core/options.h index 17f1156..2695f75 100644 --- a/yoRadio/src/core/options.h +++ b/yoRadio/src/core/options.h @@ -1,7 +1,7 @@ #ifndef options_h #define options_h -#define VERSION "0.8.089" +#define VERSION "0.8.100" /******************************************************* DO NOT EDIT THIS FILE. @@ -41,9 +41,9 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti #define DSP_SSD1305I2C 16 // 128x64 2.4' SSD1305 and SSD1309 I2C https://aliexpress.com/item/32950307344.html #define DSP_ILI9225 17 // 220x176 2.0' https://aliexpress.com/item/32952021835.html #define DSP_ST7789_240 18 // 240x240 1.3' https://aliexpress.com/item/32996979276.html -#define DSP_ST7796 19 /* !!! DSP_ST7789_240 requires further development when used in conjunction with the VS1053 module !!! See the link https://www.instructables.com/Adding-CS-Pin-to-13-LCD/ */ - +#define DSP_ST7796 19 // 480x320 3.5' https://aliexpress.com/item/1005004632953455.html?sku_id=12000029911293172 +#define DSP_GC9A01A 20 // 240x240 1.28' https://aliexpress.com/item/1005004069703494.html?sku_id=12000029869654615 #define DSP_CUSTOM 101 // your display #ifndef DSP_MODEL diff --git a/yoRadio/src/displays/conf/displayGC9A01Aconf.h b/yoRadio/src/displays/conf/displayGC9A01Aconf.h new file mode 100644 index 0000000..a12f07d --- /dev/null +++ b/yoRadio/src/displays/conf/displayGC9A01Aconf.h @@ -0,0 +1,66 @@ +/************************************************************************************* + ST7789 240x240 displays configuration file. + Copy this file to yoRadio/src/displays/conf/displayST7789conf_custom.h + and modify it + More info on https://github.com/e2002/yoradio/wiki/Widgets#widgets-description +*************************************************************************************/ + +#ifndef displayST7789conf_h +#define displayST7789conf_h + +#define DSP_WIDTH 240 +#define TFT_FRAMEWDT 8 +#define MAX_WIDTH DSP_WIDTH-TFT_FRAMEWDT*2 +#define PLMITEMS 11 +#define PLMITEMLENGHT 40 +#define PLMITEMHEIGHT 22 + +#define bootLogoTop 68 +#define HIDE_TITLE2 +/* SROLLS */ /* {{ left, top, fontsize, align }, buffsize, uppercase, width, scrolldelay, scrolldelta, scrolltime } */ +const ScrollConfig metaConf PROGMEM = {{ TFT_FRAMEWDT+12, TFT_FRAMEWDT+28+20, 3, WA_CENTER }, 140, true, MAX_WIDTH-24, 5000, 5, 30 }; +const ScrollConfig title1Conf PROGMEM = {{ TFT_FRAMEWDT, /*70*/90, 2, WA_CENTER }, 140, true, MAX_WIDTH, 5000, 4, 30 }; +const ScrollConfig title2Conf PROGMEM = {{ TFT_FRAMEWDT, 90, 2, WA_CENTER }, 140, true, MAX_WIDTH, 5000, 4, 30 }; +const ScrollConfig playlistConf PROGMEM = {{ TFT_FRAMEWDT, 112, 2, WA_LEFT }, 140, true, MAX_WIDTH, 0, 2, 30 }; +const ScrollConfig apTitleConf PROGMEM = {{ TFT_FRAMEWDT+12, TFT_FRAMEWDT+28+20, 3, WA_CENTER }, 140, false, MAX_WIDTH, 0, 4, 20 }; +const ScrollConfig apSettConf PROGMEM = {{ TFT_FRAMEWDT, 240-TFT_FRAMEWDT-16, 2, WA_LEFT }, 140, false, MAX_WIDTH, 0, 4, 30 }; +const ScrollConfig weatherConf PROGMEM = {{ TFT_FRAMEWDT+30, 36, 1, WA_LEFT }, 140, true, MAX_WIDTH-60, 0, 3, 30 }; +/* BACKGROUNDS */ /* {{ left, top, fontsize, align }, width, height, outlined } */ +const FillConfig metaBGConf PROGMEM = {{ 0, 32+20, 0, WA_LEFT }, DSP_WIDTH, 30, false }; +const FillConfig volbarConf PROGMEM = {{ TFT_FRAMEWDT+56, 240-TFT_FRAMEWDT-6, 0, WA_LEFT }, MAX_WIDTH-112, 6+TFT_FRAMEWDT+1, true }; +const FillConfig playlBGConf PROGMEM = {{ 0, 107, 0, WA_LEFT }, DSP_WIDTH, 24, false }; +const FillConfig heapbarConf PROGMEM = {{ 0, 83, 0, WA_LEFT }, DSP_WIDTH, 1, false }; + +/* WIDGETS */ /* { left, top, fontsize, align } */ +const WidgetConfig bootstrConf PROGMEM = { 0, 182, 1, WA_CENTER }; +const WidgetConfig bitrateConf PROGMEM = { 134, 20, 1, WA_RIGHT }; +const WidgetConfig voltxtConf PROGMEM = { 80, 6, 1, WA_CENTER }; +const WidgetConfig iptxtConf PROGMEM = { TFT_FRAMEWDT, 214, 1, WA_CENTER }; +const WidgetConfig rssiConf PROGMEM = { 134, 20, 1, WA_LEFT }; +const WidgetConfig numConf PROGMEM = { 0, 120+30+20, 52, WA_CENTER }; +const WidgetConfig apNameConf PROGMEM = { TFT_FRAMEWDT, 86, 2, WA_CENTER }; +const WidgetConfig apName2Conf PROGMEM = { TFT_FRAMEWDT, 110, 2, WA_CENTER }; +const WidgetConfig apPassConf PROGMEM = { TFT_FRAMEWDT, 150, 2, WA_CENTER }; +const WidgetConfig apPass2Conf PROGMEM = { TFT_FRAMEWDT, 174, 2, WA_CENTER }; +const WidgetConfig clockConf PROGMEM = { 0, 176, 52, WA_CENTER }; /* 52 is a fixed font size. do not change */ +const WidgetConfig vuConf PROGMEM = { TFT_FRAMEWDT+10, 188, 1, WA_CENTER }; + +const WidgetConfig bootWdtConf PROGMEM = { 0, 162, 1, WA_CENTER }; +const ProgressConfig bootPrgConf PROGMEM = { 90, 14, 4 }; + +/* BANDS */ /* { onebandwidth, onebandheight, bandsHspace, bandsVspace, numofbands, fadespeed } */ +const VUBandsConfig bandsConf PROGMEM = { 90, 20, 10, 2, 10, 5 }; + +/* STRINGS */ +const char numtxtFmt[] PROGMEM = "%d"; +const char rssiFmt[] PROGMEM = "WIFI %d"; +const char iptxtFmt[] PROGMEM = "%s"; +const char voltxtFmt[] PROGMEM = "VOL %d"; +const char bitrateFmt[] PROGMEM = "%d KBS"; + +/* MOVES */ /* { left, top, width } */ +const MoveConfig clockMove PROGMEM = { 0, 164, 0 }; +const MoveConfig weatherMove PROGMEM = { TFT_FRAMEWDT, 202, -1 }; +const MoveConfig weatherMoveVU PROGMEM = { TFT_FRAMEWDT, 202, -1/*MAX_WIDTH*/ }; + +#endif diff --git a/yoRadio/src/displays/displayGC9A01A.cpp b/yoRadio/src/displays/displayGC9A01A.cpp new file mode 100644 index 0000000..bbb64da --- /dev/null +++ b/yoRadio/src/displays/displayGC9A01A.cpp @@ -0,0 +1,226 @@ +#include "../core/options.h" +#if DSP_MODEL==DSP_GC9A01A + +#include "displayGC9A01A.h" +//#include +#include "fonts/bootlogo.h" +#include "../core/player.h" +#include "../core/config.h" +#include "../core/network.h" + +#ifndef DEF_SPI_FREQ + #define DEF_SPI_FREQ 40000000UL /* set it to 0 for system default */ +#endif + +#define TAKE_MUTEX() if(player.mutex_pl) xSemaphoreTake(player.mutex_pl, portMAX_DELAY) +#define GIVE_MUTEX() if(player.mutex_pl) xSemaphoreGive(player.mutex_pl) + +#if DSP_HSPI +DspCore::DspCore(): Adafruit_GC9A01A(&SPI2, TFT_CS, TFT_DC, TFT_RST) {} +#else +DspCore::DspCore(): Adafruit_GC9A01A(TFT_CS, TFT_DC, TFT_RST) {} +#endif + +#include "tools/utf8RusGFX.h" + +void DspCore::initDisplay() { + begin(); + if(DEF_SPI_FREQ > 0) setSPISpeed(DEF_SPI_FREQ); + invert(); + cp437(true); + flip(); + setTextWrap(false); + setTextSize(1); + fillScreen(0x0000); +} + +void DspCore::drawLogo(uint16_t top) { drawRGBBitmap((width() - 99) / 2, top, bootlogo2, 99, 64); } + +void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) { + for (byte i = 0; i < PLMITEMS; i++) { + plMenu[i][0] = '\0'; + } + config.fillPlMenu(plMenu, currentItem - 5, PLMITEMS); + setTextSize(2); + int yStart = (height() / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3; + + for (byte i = 0; i < PLMITEMS; i++) { + if (i == 5) { + strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1); + } else { + setTextColor(config.theme.playlist[abs(i - 5)-1], config.theme.background); + setCursor(TFT_FRAMEWDT, yStart + i * PLMITEMHEIGHT); + fillRect(0, yStart + i * PLMITEMHEIGHT - 1, width(), PLMITEMHEIGHT - 2, config.theme.background); + print(utf8Rus(plMenu[i], true)); + } + } +} + +void DspCore::clearDsp(bool black) { fillScreen(black?0:config.theme.background); } + +GFXglyph *pgm_read_glyph_ptr(const GFXfont *gfxFont, uint8_t c) { + return gfxFont->glyph + c; +} + +uint8_t DspCore::_charWidth(unsigned char c){ + GFXglyph *glyph = pgm_read_glyph_ptr(&DS_DIGI42pt7b, c - 0x20); + return pgm_read_byte(&glyph->xAdvance); +} + +uint16_t DspCore::textWidth(const char *txt){ + uint16_t w = 0, l=strlen(txt); + for(uint16_t c=0;c0) + dsp.fillRect(_olddateleft, clockTop+10, _olddatewidth, CHARHEIGHT, config.theme.background); + setTextColor(config.theme.date, config.theme.background); + setCursor(_dateleft, clockTop+10); + print(_dateBuf); /* print date */ + strlcpy(_oldDateBuf, _dateBuf, sizeof(_dateBuf)); + _olddatewidth = _datewidth; + _olddateleft = _dateleft; + setTextSize(3); + setTextColor(config.theme.dow, config.theme.background); + setCursor(width() - 8 - clockRightSpace - CHARWIDTH*3*2, clockTop-CHARHEIGHT*3+4); + print(utf8Rus(dow[network.timeinfo.tm_wday], false)); /* print dow */ +} + +void DspCore::_clockTime(){ + if(_oldtimeleft>0) dsp.fillRect(_oldtimeleft, clockTop-clockTimeHeight+1, _oldtimewidth, clockTimeHeight, config.theme.background); + _timeleft = width()-clockRightSpace-CHARWIDTH*3*2-24-_timewidth; + clearClock(); + setTextSize(1); + setFont(&DS_DIGI42pt7b); + setTextColor(config.theme.clock, config.theme.background); + setCursor(_timeleft, clockTop); + print(_timeBuf); + setFont(); + strlcpy(_oldTimeBuf, _timeBuf, sizeof(_timeBuf)); + _oldtimewidth = _timewidth; + _oldtimeleft = _timeleft; + drawFastVLine(width()-clockRightSpace-CHARWIDTH*3*2-18, clockTop-clockTimeHeight, clockTimeHeight+3, config.theme.div); /*divider vert*/ + drawFastHLine(width()-clockRightSpace-CHARWIDTH*3*2-18, clockTop-clockTimeHeight+29, 44, config.theme.div); /*divider hor*/ + sprintf(_buffordate, "%2d %s %d", network.timeinfo.tm_mday,mnths[network.timeinfo.tm_mon], network.timeinfo.tm_year+1900); + strlcpy(_dateBuf, utf8Rus(_buffordate, true), sizeof(_dateBuf)); + _datewidth = strlen(_dateBuf) * CHARWIDTH; + //_dateleft = width() - 8 - clockRightSpace - _datewidth; + _dateleft = (width() - _datewidth)/2; +} + +void DspCore::printClock(uint16_t top, uint16_t rightspace, uint16_t timeheight, bool redraw){ + clockTop = top; + clockRightSpace = (width()-_timewidth-CHARWIDTH*3*2-36)/2;//rightspace; + clockTimeHeight = timeheight; + strftime(_timeBuf, sizeof(_timeBuf), "%H:%M", &network.timeinfo); + if(strcmp(_oldTimeBuf, _timeBuf)!=0 || redraw){ + _getTimeBounds(); + clockRightSpace = (width()-_timewidth-CHARWIDTH*3*2-36)/2; + _clockTime(); + /*if(strcmp(_oldDateBuf, _dateBuf)!=0 || redraw) */_clockDate(); + } + _clockSeconds(); +} + +void DspCore::clearClock(){ + if(_oldtimeleft>0){ + dsp.fillRect(_oldtimeleft, clockTop-clockTimeHeight, _oldtimewidth+CHARWIDTH*3*2+24, clockTimeHeight+10+CHARHEIGHT, config.theme.background); + }else{ + dsp.fillRect(_timeleft, clockTop-clockTimeHeight, _timewidth+CHARWIDTH*3*2+24, clockTimeHeight+10+CHARHEIGHT, config.theme.background); + } +} + +void DspCore::startWrite(void) { + TAKE_MUTEX(); + Adafruit_GC9A01A::startWrite(); +} + +void DspCore::endWrite(void) { + Adafruit_GC9A01A::endWrite(); + GIVE_MUTEX(); +} + +void DspCore::loop(bool force) { + +} + +void DspCore::charSize(uint8_t textsize, uint8_t& width, uint16_t& height){ + width = textsize * CHARWIDTH; + height = textsize * CHARHEIGHT; +} + +void DspCore::setTextSize(uint8_t s){ + Adafruit_GFX::setTextSize(s); +} + +void DspCore::flip(){ + if(ROTATE_90){ + setRotation(config.store.flipscreen?3:1); + }else{ + setRotation(config.store.flipscreen?2:0); + } +} + +void DspCore::invert(){ + invertDisplay(!config.store.invertdisplay); +} + +void DspCore::sleep(void) { + enableSleep(true); delay(150); enableDisplay(false); delay(150); +} +void DspCore::wake(void) { + enableDisplay(true); delay(150); enableSleep(false); delay(150); +} + + +void DspCore::writePixel(int16_t x, int16_t y, uint16_t color) { + if(_clipping){ + if ((x < _cliparea.left) || (x > _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return; + } + Adafruit_GC9A01A::writePixel(x, y, color); +} + +void DspCore::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) { + if(_clipping){ + if ((x < _cliparea.left) || (x >= _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return; + } + Adafruit_GC9A01A::writeFillRect(x, y, w, h, color); +} + +void DspCore::setClipping(clipArea ca){ + _cliparea = ca; + _clipping = true; +} + +void DspCore::clearClipping(){ + _clipping = false; +} + +void DspCore::setNumFont(){ + setFont(&DS_DIGI42pt7b); + setTextSize(1); +} +#endif diff --git a/yoRadio/src/displays/displayGC9A01A.h b/yoRadio/src/displays/displayGC9A01A.h new file mode 100644 index 0000000..f6c39b2 --- /dev/null +++ b/yoRadio/src/displays/displayGC9A01A.h @@ -0,0 +1,34 @@ +#ifndef displayGC9A01A_h +#define displayGC9A01A_h +#include "../core/options.h" + +#include "Arduino.h" +#include +#include "../Adafruit_GC9A01A/Adafruit_GC9A01A.h" +#include "fonts/DS_DIGI42pt7b.h" // https://tchapi.github.io/Adafruit-GFX-Font-Customiser/ +#include "tools/l10n.h" + +#define CHARWIDTH 6 +#define CHARHEIGHT 8 + +typedef GFXcanvas16 Canvas; +#include "widgets/widgets.h" +#include "widgets/pages.h" + +#if __has_include("conf/displayGC9A01Aconf_custom.h") + #include "conf/displayGC9A01Aconf_custom.h" +#else + #include "conf/displayGC9A01Aconf.h" +#endif + +#define BOOT_PRG_COLOR 0xE68B +#define BOOT_TXT_COLOR 0xFFFF +#define PINK 0xF97F + +class DspCore: public Adafruit_GC9A01A { +#include "tools/commongfx.h" +}; + +extern DspCore dsp; + +#endif diff --git a/yoRadio/src/displays/dspcore.h b/yoRadio/src/displays/dspcore.h index 769ae03..13d8b29 100644 --- a/yoRadio/src/displays/dspcore.h +++ b/yoRadio/src/displays/dspcore.h @@ -36,6 +36,8 @@ #include "displayILI9225.h" #elif DSP_MODEL==DSP_ST7796 #include "displayST7796.h" +#elif DSP_MODEL==DSP_GC9A01A + #include "displayGC9A01A.h" #endif //extern DspCore dsp;