diff --git a/yoRadio/src/SSD1322/SSD1322.cpp b/yoRadio/src/SSD1322/SSD1322.cpp new file mode 100644 index 0000000..5d75d58 --- /dev/null +++ b/yoRadio/src/SSD1322/SSD1322.cpp @@ -0,0 +1,554 @@ +/*! + * @file Jamis_SSD1322.h + * + * This is a partial port of Adafruit's SSD1306 library to the SSD1322 + * display. + * + * These displays use SPI to communicate. SPI requires 4 pins (MOSI, SCK, + * select, data/command) and optionally a reset pin. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Written by Limor Fried/Ladyada for Adafruit Industries, with + * contributions from the open source community. + * + * BSD license, all text above, and the splash screen header file, + * must be included in any redistribution. + * + */ +#include "../core/options.h" +#if DSP_MODEL==DSP_SSD1322 + + +#define pgm_read_byte(addr) \ + (*(const unsigned char *)(addr)) ///< PROGMEM workaround for non-AVR + +#include +#include "SSD1322.h" +//#include "splash.h" + +#define ssd1322_swap(a, b) \ + (((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b))) ///< No-temp-var swap operation + +#define SSD1322_SELECT digitalWrite(csPin, LOW); ///< Device select +#define SSD1322_DESELECT digitalWrite(csPin, HIGH); ///< Device deselect +#define SSD1322_MODE_COMMAND digitalWrite(dcPin, LOW); ///< Command mode +#define SSD1322_MODE_DATA digitalWrite(dcPin, HIGH); ///< Data mode + +#if defined(SPI_HAS_TRANSACTION) + #define SPI_TRANSACTION_START spi->beginTransaction(spiSettings) ///< Pre-SPI + #define SPI_TRANSACTION_END spi->endTransaction() ///< Post-SPI +#else // SPI transactions likewise not present in older Arduino SPI lib + #define SPI_TRANSACTION_START ///< Dummy stand-in define + #define SPI_TRANSACTION_END ///< keeps compiler happy +#endif + +#define TRANSACTION_START \ + if(spi) { \ + SPI_TRANSACTION_START; \ + } \ + SSD1322_SELECT; +#define TRANSACTION_END \ + SSD1322_DESELECT; \ + if(spi) { \ + SPI_TRANSACTION_END; \ + } + +/*! + @brief Constructor for SPI SSD1306 displays, using native hardware SPI. + @param w + Display width in pixels + @param h + Display height in pixels + @param spi + Pointer to an existing SPIClass instance (e.g. &SPI, the + microcontroller's primary SPI bus). + @param dc_pin + Data/command pin (using Arduino pin numbering), selects whether + display is receiving commands (low) or data (high). + @param rst_pin + Reset pin (using Arduino pin numbering), or -1 if not used + (some displays might be wired to share the microcontroller's + reset pin). + @param cs_pin + Chip-select pin (using Arduino pin numbering) for sharing the + bus with other devices. Active low. + @param bitrate + SPI clock rate for transfers to this display. Default if + unspecified is 8000000UL (8 MHz). + @return Jamis_SSD1322 object. + @note Call the object's begin() function before use -- buffer + allocation is performed there! +*/ +Jamis_SSD1322::Jamis_SSD1322(int16_t w, int16_t h, SPIClass *spi, + int8_t dc_pin, int8_t rst_pin, int8_t cs_pin, uint32_t bitrate) : + Adafruit_GFX(w, h), spi(spi ? spi : &SPI), buffer(NULL), + mosiPin(-1), clkPin(-1), dcPin(dc_pin), csPin(cs_pin), rstPin(rst_pin) { +#ifdef SPI_HAS_TRANSACTION + spiSettings = SPISettings(bitrate, MSBFIRST, SPI_MODE3); +#endif +} + +/*! + @brief Destructor for Jamis_SSD1322 object. +*/ +Jamis_SSD1322::~Jamis_SSD1322(void) { + if(buffer) { + free(buffer); + buffer = NULL; + } +} + +// LOW-LEVEL UTILS --------------------------------------------------------- + +// Issue single byte out SPI, either soft or hardware as appropriate. +// SPI transaction/selection must be performed in calling function. +inline void Jamis_SSD1322::SPIwrite(uint8_t d) { + spi->transfer(d); +} + +// Issue single command to SSD1322, using hard SPI. +// Because command calls are often grouped, SPI transaction and selection +// must be started/ended in calling function for efficiency. +// This is a private function, not exposed (see ssd1322_command() instead). +void Jamis_SSD1322::ssd1322_command1(uint8_t c) { + SSD1322_MODE_COMMAND + SPIwrite(c); +} + +// Issue single byte of data out SPI. +void Jamis_SSD1322::ssd1322_data1(uint8_t c) { + SSD1322_MODE_DATA + SPIwrite(c); +} + +// A public version of ssd1322_command1(), for existing user code that +// might rely on that function. This encapsulates the command transfer +// in a transaction start/end, similar to old library's handling of it. +/*! + @brief Issue a single low-level command directly to the SSD1306 + display, bypassing the library. + @param c + Command to issue (0x00 to 0xFF, see datasheet). + @return None (void). +*/ +void Jamis_SSD1322::ssd1322_command(uint8_t c) { + TRANSACTION_START + ssd1322_command1(c); + TRANSACTION_END +} + +// ALLOCATE & INIT DISPLAY ------------------------------------------------- + +/*! + @brief Allocate RAM for image buffer, initialize peripherals and pins. + @param reset + If true, and if the reset pin passed to the constructor is + valid, a hard reset will be performed before initializing the + display. If using multiple SSD1322 displays on the same bus, and + if they all share the same reset pin, you should only pass true + on the first display being initialized, false on all others, + else the already-initialized displays would be reset. Default if + unspecified is true. + @param periphBegin + If true, and if a hardware peripheral is being used (I2C or SPI, + but not software SPI), call that peripheral's begin() function, + else (false) it has already been done in one's sketch code. + Cases where false might be used include multiple displays or + other devices sharing a common bus, or situations on some + platforms where a nonstandard begin() function is available + (e.g. a TwoWire interface on non-default pins, as can be done + on the ESP8266 and perhaps others). + @return true on successful allocation/init, false otherwise. + Well-behaved code should check the return value before + proceeding. + @note MUST call this function before any drawing or updates! +*/ +boolean Jamis_SSD1322::begin(boolean reset, boolean periphBegin) { + // Note: The SSD1322 has 4 bit grayscale color. + if((!buffer) && !(buffer = (uint8_t *)malloc( WIDTH * ((HEIGHT) / 2) ))) + return false; + + clearDisplay(); + + + // Setup pin directions + pinMode(dcPin, OUTPUT); // Set data/command pin as output + pinMode(csPin, OUTPUT); // Same for chip select + SSD1322_DESELECT + + // SPI peripheral begin same as wire check above. + if(periphBegin) spi->begin(); + + // Reset SSD1322 if requested and reset pin specified in constructor + if(reset && (rstPin >= 0)) { + pinMode( rstPin, OUTPUT); + digitalWrite(rstPin, HIGH); + delay(1); // VDD goes high at start, pause for 1 ms + digitalWrite(rstPin, LOW); // Bring reset low + delay(10); // Wait 10 ms + digitalWrite(rstPin, HIGH); // Bring out of reset + } + + TRANSACTION_START + + ssd1322_command1(0xFD); // Set Command Lock (MCU protection status) + ssd1322_data1(0x12); // 0x12 = Unlock Basic Commands; 0x16 = lock + + ssd1322_command1(0xA4); // Set Display Mode = OFF + + ssd1322_command1(0xB3); // Set Front Clock Divider / Oscillator Frequency + ssd1322_data1(0x91); // 0x91 = 80FPS; 0xD0 = default / 1100b + + ssd1322_command1(0xCA); // Set MUX Ratio + ssd1322_data1(0x3F); // 0x3F = 63d = 64MUX (1/64 duty cycle) + + ssd1322_command1(0xA2); // Set Display Offset + ssd1322_data1(0x00); // 0x00 = (default) + + ssd1322_command1(0xA1); // Set Display Start Line + ssd1322_data1(0x00); // 0x00 = register 00h + + ssd1322_command1(0xA0); // Set Re-map and Dual COM Line mode + ssd1322_data1(0x14); // 0x14 = Default except Enable Nibble Re-map, Scan from COM[N-1] to COM0, where N is the Multiplex ratio + ssd1322_data1(0x11); // 0x11 = Enable Dual COM mode (MUX <= 63) + + ssd1322_command1(0xB5); // Set GPIO + ssd1322_data1(0x00); // 0x00 = {GPIO0, GPIO1 = HiZ (Input Disabled)} + + ssd1322_command1(0xAB); // Function Selection + ssd1322_data1(0x01); // 0x01 = Enable internal VDD regulator (default) + + ssd1322_command1(0xB4); // Display Enhancement A + ssd1322_data1(0xA0); // 0xA0 = Enable external VSL; 0xA2 = internal VSL + ssd1322_data1(0xB5); // 0xB5 = Normal (default); 0xFD = 11111101b = Enhanced low GS display quality + + ssd1322_command1(0xC1); // Set Contrast Current + ssd1322_data1(0x7F); // 0x7F = (default) + + ssd1322_command1(0xC7); // Master Contrast Current Control + ssd1322_data1(0x0F); // 0x0F = (default) + + ssd1322_command1(0xB9); // Select Default Linear Gray Scale table + + // ssd1322_command1(0xB8); // Select Custom Gray Scale table (GS0 = 0) + // ssd1322_command1(0x00); // GS1 + // ssd1322_command1(0x08); // GS2 + // ssd1322_command1(0x10); // GS3 + // ssd1322_command1(0x18); // GS4 + // ssd1322_command1(0x20); // GS5 + // ssd1322_command1(0x28); // GS6 + // ssd1322_command1(0x30); // GS7 + // ssd1322_command1(0x38); // GS8 + // ssd1322_command1(0x40); // GS9 + // ssd1322_command1(0x48); // GS10 + // ssd1322_command1(0x50); // GS11 + // ssd1322_command1(0x58); // GS12 + // ssd1322_command1(0x60); // GS13 + // ssd1322_command1(0x68); // GS14 + // ssd1322_command1(0x70); // GS15 + // ssd1322_command1(0x00); // Enable Custom Gray Scale table + + ssd1322_command1(0xB1); // Set Phase Length + ssd1322_data1(0xE2); // 0xE2 = Phase 1 period (reset phase length) = 5 DCLKs, + // Phase 2 period (first pre-charge phase length) = 14 DCLKs + + ssd1322_command1(0xD1); // Display Enhancement B + ssd1322_data1(0xA2); // 0xA2 = Normal (default); 0x82 = reserved + ssd1322_data1(0x20); // 0x20 = as-is + + ssd1322_command1(0xBB); // Set Pre-charge voltage + ssd1322_data1(0x1F); // 0x17 = default; 0x1F = 0.60*Vcc (spec example) + + ssd1322_command1(0xB6); // Set Second Precharge Period + ssd1322_data1(0x08); // 0x08 = 8 dclks (default) + + ssd1322_command1(0xBE); // Set VCOMH + ssd1322_data1(0x07); // 0x04 = 0.80*Vcc (default); 0x07 = 0.86*Vcc (spec example) + + ssd1322_command1(0xA6); // Set Display Mode = Normal Display + + ssd1322_command1(0xA9); // Exit Partial Display + + ssd1322_command1(0xAF); // Set Sleep mode OFF (Display ON) + + TRANSACTION_END + + return true; // Success +} + +// DRAWING FUNCTIONS ------------------------------------------------------- + +/*! + @brief Set/clear/invert a single pixel. This is also invoked by the + Adafruit_GFX library in generating many higher-level graphics + primitives. + @param x + Column of display -- 0 at left to (screen width - 1) at right. + @param y + Row of display -- 0 at top to (screen height -1) at bottom. + @param color + Pixel color, one of: BLACK, WHITE or INVERT. + @return None (void). + @note Changes buffer contents only, no immediate effect on display. + Follow up with a call to display(), or with other graphics + commands as needed by one's own application. +*/ +void Jamis_SSD1322::drawPixel(int16_t x, int16_t y, uint16_t color) { + if((x >= 0) && (x < width()) && (y >= 0) && (y < height())) { + // Pixel is in-bounds. Rotate coordinates if needed. + switch(getRotation()) { + case 1: + ssd1322_swap(x, y); + x = WIDTH - x - 1; + break; + case 2: + x = WIDTH - x - 1; + y = HEIGHT - y - 1; + break; + case 3: + ssd1322_swap(x, y); + y = HEIGHT - y - 1; + break; + } + buffer[(x >> 1) + (y)*WIDTH/2] &= (x % 2) ? 0xF0 : 0x0F; + buffer[(x >> 1) + (y)*WIDTH/2] |= (color << (!(x & 1) * 4) ); + } +} + + +/*! + @brief Clear contents of display buffer (set all pixels to off). + @return None (void). + @note Changes buffer contents only, no immediate effect on display. + Follow up with a call to display(), or with other graphics + commands as needed by one's own application. +*/ +void Jamis_SSD1322::clearDisplay(void) { + memset(buffer, 0, WIDTH * ((HEIGHT) / 2)); +} + +/*! + @brief Draw a horizontal line. This is also invoked by the Adafruit_GFX + library in generating many higher-level graphics primitives. + @param x + Leftmost column -- 0 at left to (screen width - 1) at right. + @param y + Row of display -- 0 at top to (screen height -1) at bottom. + @param w + Width of line, in pixels. + @param color + Line color, one of: BLACK, WHITE or INVERT. + @return None (void). + @note Changes buffer contents only, no immediate effect on display. + Follow up with a call to display(), or with other graphics + commands as needed by one's own application. +*/ +void Jamis_SSD1322::drawFastHLine( + int16_t x, int16_t y, int16_t w, uint16_t color) { + boolean bSwap = false; + switch(rotation) { + case 1: + // 90 degree rotation, swap x & y for rotation, then invert x + bSwap = true; + ssd1322_swap(x, y); + x = WIDTH - x - 1; + break; + case 2: + // 180 degree rotation, invert x and y, then shift y around for height. + x = WIDTH - x - 1; + y = HEIGHT - y - 1; + x -= (w-1); + break; + case 3: + // 270 degree rotation, swap x & y for rotation, + // then invert y and adjust y for w (not to become h) + bSwap = true; + ssd1322_swap(x, y); + y = HEIGHT - y - 1; + y -= (w-1); + break; + } + + if(bSwap) drawFastVLineInternal(x, y, w, color); + else drawFastHLineInternal(x, y, w, color); +} + +void Jamis_SSD1322::drawFastHLineInternal( + int16_t x, int16_t y, int16_t w, uint16_t color) { + + if((y >= 0) && (y < HEIGHT)) { // Y coord in bounds? + if(x < 0) { // Clip left + w += x; + x = 0; + } + if((x + w) > WIDTH) { // Clip right + w = (WIDTH - x); + } + if(w > 0) { // Proceed only if width is positive + // NOTE: This is _not_ fast. But with 4bit packing, I just want this done. + uint16_t yOffset = (y)*WIDTH/2; + uint8_t b1 = (x % 2) ? 0xF0 : 0x0F; + while(w--) { + buffer[((x + w) >> 1) + yOffset] &= b1; + buffer[((x + w) >> 1) + yOffset] |= (color << (!((x + w) & 1) * 4) ); + } + } + } +} + +/*! + @brief Draw a vertical line. This is also invoked by the Adafruit_GFX + library in generating many higher-level graphics primitives. + @param x + Column of display -- 0 at left to (screen width -1) at right. + @param y + Topmost row -- 0 at top to (screen height - 1) at bottom. + @param h + Height of line, in pixels. + @param color + Line color, one of: BLACK, WHITE or INVERT. + @return None (void). + @note Changes buffer contents only, no immediate effect on display. + Follow up with a call to display(), or with other graphics + commands as needed by one's own application. +*/ +void Jamis_SSD1322::drawFastVLine( + int16_t x, int16_t y, int16_t h, uint16_t color) { + boolean bSwap = false; + switch(rotation) { + case 1: + // 90 degree rotation, swap x & y for rotation, + // then invert x and adjust x for h (now to become w) + bSwap = true; + ssd1322_swap(x, y); + x = WIDTH - x - 1; + x -= (h-1); + break; + case 2: + // 180 degree rotation, invert x and y, then shift y around for height. + x = WIDTH - x - 1; + y = HEIGHT - y - 1; + y -= (h-1); + break; + case 3: + // 270 degree rotation, swap x & y for rotation, then invert y + bSwap = true; + ssd1322_swap(x, y); + y = HEIGHT - y - 1; + break; + } + + if(bSwap) drawFastHLineInternal(x, y, h, color); + else drawFastVLineInternal(x, y, h, color); +} + +void Jamis_SSD1322::drawFastVLineInternal( + int16_t x, int16_t __y, int16_t __h, uint16_t color) { + + if((x >= 0) && (x < WIDTH)) { // X coord in bounds? + if(__y < 0) { // Clip top + __h += __y; + __y = 0; + } + if((__y + __h) > HEIGHT) { // Clip bottom + __h = (HEIGHT - __y); + } + if(__h > 0) { // Proceed only if height is now positive + + //buffer[((x + w) >> 1) + yOffset] |= (color << (!(w & 1) * 4) ); + uint16_t xOffset = (x >> 1); + uint16_t mask = (color << (!(x & 1) * 4) ); + uint8_t b1 = (x % 2) ? 0xF0 : 0x0F; + while(__h--) { + //Serial.printf("xOffset + (__y+__h)*WIDTH/2=%d, WIDTH=%d\n", xOffset + (__y+__h)*WIDTH/2, WIDTH); + buffer[xOffset + (__y+__h)*WIDTH/2] &= b1; + buffer[xOffset + (__y+__h)*WIDTH/2] |= mask; + } + + + } // endif positive height + } // endif x in bounds +} + +/*! + @brief Return color of a single pixel in display buffer. + @param x + Column of display -- 0 at left to (screen width - 1) at right. + @param y + Row of display -- 0 at top to (screen height -1) at bottom. + @return true if pixel is set (usually WHITE, unless display invert mode + is enabled), false if clear (BLACK). + @note Reads from buffer contents; may not reflect current contents of + screen if display() has not been called. +*/ +boolean Jamis_SSD1322::getPixel(int16_t x, int16_t y) { + if((x >= 0) && (x < width()) && (y >= 0) && (y < height())) { + // Pixel is in-bounds. Rotate coordinates if needed. + switch(getRotation()) { + case 1: + ssd1322_swap(x, y); + x = WIDTH - x - 1; + break; + case 2: + x = WIDTH - x - 1; + y = HEIGHT - y - 1; + break; + case 3: + ssd1322_swap(x, y); + y = HEIGHT - y - 1; + break; + } + return (buffer[x + (y / 8) * WIDTH] & (1 << (y & 7))); + } + return false; // Pixel out of bounds +} + +/*! + @brief Get base address of display buffer for direct reading or writing. + @return Pointer to an unsigned 8-bit array, column-major, columns padded + to full byte boundary if needed. +*/ +uint8_t *Jamis_SSD1322::getBuffer(void) { + return buffer; +} + +// REFRESH DISPLAY --------------------------------------------------------- + +/*! + @brief Push data currently in RAM to SSD1306 display. + @return None (void). + @note Drawing operations are not visible until this function is + called. Call after each graphics command, or after a whole set + of graphics commands, as best needed by one's own application. +*/ +void Jamis_SSD1322::display(void) { + TRANSACTION_START + + // Set column address: Set_Column_Address(0x00, MAXCOLS-1); + ssd1322_command1(0x15); + // Each Column address holds 4 horizontal pixels worth of data + const uint16_t Col0Off = 0x70; + const uint16_t ColDiv = 4; + ssd1322_data1( (Col0Off+0x00)/ColDiv ); + ssd1322_data1( (Col0Off+WIDTH-1)/ColDiv ); + + // Set row address: Set_Row_Address(0x00, MAXROWS-1); + ssd1322_command1(0x75); + ssd1322_data1(0x00); + ssd1322_data1(HEIGHT-1); + + // Enable writing into MCU RAM: Set_Write_RAM(); + ssd1322_command1(0x5C); + + uint16_t count = WIDTH * ((HEIGHT) / 2); + //Serial.printlnf("%i", count); + uint8_t *ptr = buffer; + SSD1322_MODE_DATA + while(count--) SPIwrite(*ptr++); + TRANSACTION_END +} + +#endif //if DSP_MODEL==DSP_SSD1322 diff --git a/yoRadio/src/SSD1322/SSD1322.h b/yoRadio/src/SSD1322/SSD1322.h new file mode 100644 index 0000000..69d23e3 --- /dev/null +++ b/yoRadio/src/SSD1322/SSD1322.h @@ -0,0 +1,83 @@ +/*! + * @file Jamis_SSD1322.h + * + * This is a partial port of Adafruit's SSD1306 library to the SSD1322 + * display. + * + * These displays use SPI to communicate. SPI requires 4 pins (MOSI, SCK, + * select, data/command) and optionally a reset pin. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Written by Limor Fried/Ladyada for Adafruit Industries, with + * contributions from the open source community. + * + * BSD license, all text above, and the splash screen header file, + * must be included in any redistribution. + * + */ + +#ifndef _Jamis_SSD1322_H_ +#define _Jamis_SSD1322_H_ + +#if defined(PARTICLE) +#include "Particle.h" +#define SPI_HAS_TRANSACTION +#define SPISettings __SPISettings +#define BUFFER_LENGTH 32 +#else +#include +#include +#endif + +#include + +#define BLACK 0 ///< Draw 'off' pixels +#define WHITE 0xf ///< Draw 'on' pixels +#define INVERSE 2 ///< Invert pixels + +#define SSD1322_DISPLAYOFF 0xAE +#define SSD1322_DISPLAYON 0xAF + +/*! + @brief Class that stores state and functions for interacting with + SSD1322 OLED displays. +*/ +class Jamis_SSD1322 : public Adafruit_GFX { + public: + Jamis_SSD1322(int16_t w, int16_t h, SPIClass *spi, int8_t dc_pin, + int8_t rst_pin, int8_t cs_pin, uint32_t bitrate=8000000UL); + + ~Jamis_SSD1322(void); + + boolean begin(boolean reset=true, boolean periphBegin=true); + void display(void); + void clearDisplay(void); + void drawPixel(int16_t x, int16_t y, uint16_t color); + virtual void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); + virtual void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); + void ssd1322_command(uint8_t c); + boolean getPixel(int16_t x, int16_t y); + uint8_t *getBuffer(void); + void oled_command(uint8_t c) { ssd1322_command1(c); } + private: + inline void SPIwrite(uint8_t d) __attribute__((always_inline)); + void drawFastHLineInternal(int16_t x, int16_t y, int16_t w, + uint16_t color); + void drawFastVLineInternal(int16_t x, int16_t y, int16_t h, + uint16_t color); + void ssd1322_command1(uint8_t c); + void ssd1322_data1(uint8_t c); + void ssd1322_commandList(const uint8_t *c, uint8_t n); + + SPIClass *spi; + uint8_t *buffer; + int8_t mosiPin , clkPin , dcPin , csPin, rstPin; +#if defined(SPI_HAS_TRANSACTION) + SPISettings spiSettings; +#endif +}; + +#endif // _Jamis_SSD1322_H_ diff --git a/yoRadio/src/core/options.h b/yoRadio/src/core/options.h index 6bdcf2e..76c8f41 100644 --- a/yoRadio/src/core/options.h +++ b/yoRadio/src/core/options.h @@ -1,7 +1,7 @@ #ifndef options_h #define options_h -#define YOVERSION "0.9.022" +#define YOVERSION "0.9.042" /******************************************************* DO NOT EDIT THIS FILE. @@ -48,6 +48,7 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti #define DSP_GC9A01A 20 // 240x240 1.28' https://aliexpress.com/item/1005004069703494.html?sku_id=12000029869654615 #define DSP_ILI9488 21 // 480x320 3.5' https://aliexpress.com/item/1005001999296476.html?sku_id=12000018365356570 #define DSP_ILI9486 22 // (Testing mode) 480x320 3.5' https://aliexpress.com/item/1005001999296476.html?sku_id=12000018365356568 +#define DSP_SSD1322 23 // 256x64 2.8' https://aliexpress.com/item/1005003480981568.html #define DSP_CUSTOM 101 // your display #ifndef DSP_MODEL diff --git a/yoRadio/src/displays/conf/displaySSD1322conf.h b/yoRadio/src/displays/conf/displaySSD1322conf.h new file mode 100644 index 0000000..b38e57d --- /dev/null +++ b/yoRadio/src/displays/conf/displaySSD1322conf.h @@ -0,0 +1,70 @@ +/************************************************************************************* + SSD1305 128x64 displays configuration file. + Copy this file to yoRadio/src/displays/conf/displaySSD1305conf_custom.h + and modify it + More info on https://github.com/e2002/yoradio/wiki/Widgets#widgets-description +*************************************************************************************/ + +#ifndef displaySSD1322conf_h +#define displaySSD1322conf_h + +#define DSP_WIDTH 256 +#define TFT_FRAMEWDT 1 +#define MAX_WIDTH DSP_WIDTH-TFT_FRAMEWDT*2 + +#define HIDE_HEAPBAR +#define HIDE_VOL +#define HIDE_VU +//#define HIDE_TITLE2 + +#define bootLogoTop 68 + +/* SROLLS */ /* {{ left, top, fontsize, align }, buffsize, uppercase, width, scrolldelay, scrolldelta, scrolltime } */ +const ScrollConfig metaConf PROGMEM = {{ TFT_FRAMEWDT+1, TFT_FRAMEWDT+1, 2, WA_LEFT }, 140, true, MAX_WIDTH-2, 5000, 2, 25 }; +const ScrollConfig title1Conf PROGMEM = {{ 0, 20, 1, WA_LEFT }, 140, true, DSP_WIDTH/2+6, 5000, 2, 25 }; +const ScrollConfig title2Conf PROGMEM = {{ 0, 29, 1, WA_LEFT }, 140, true, DSP_WIDTH/2+6, 5000, 2, 25 }; +const ScrollConfig playlistConf PROGMEM = {{ TFT_FRAMEWDT, 30, 1, WA_LEFT }, 140, true, MAX_WIDTH, 500, 2, 25 }; +const ScrollConfig apTitleConf PROGMEM = {{ TFT_FRAMEWDT+1, TFT_FRAMEWDT+1, 1, WA_CENTER }, 140, false, MAX_WIDTH-2, 0, 2, 25 }; +const ScrollConfig apSettConf PROGMEM = {{ TFT_FRAMEWDT, 64-7, 1, WA_LEFT }, 140, false, MAX_WIDTH, 0, 2, 25 }; +const ScrollConfig weatherConf PROGMEM = {{ 0, 64-12, 1, WA_LEFT }, 140, true, DSP_WIDTH/2+6, 0, 2, 25 }; // ПОГОДА!! + +/* BACKGROUNGC9106DS */ /* {{ left, top, fontsize, align }, width, height, outlined } */ +const FillConfig metaBGConf PROGMEM = {{ 0, 0, 0, WA_LEFT }, DSP_WIDTH, 18, false }; +const FillConfig volbarConf PROGMEM = {{ 0, 64-1-1-1, 0, WA_LEFT }, DSP_WIDTH, 3, true }; +const FillConfig playlBGConf PROGMEM = {{ 0, 26, 0, WA_LEFT }, DSP_WIDTH, 12, false }; +const FillConfig heapbarConf PROGMEM = {{ 0, 63, 0, WA_LEFT }, DSP_WIDTH, 1, false }; + +/* WIDGETS */ /* { left, top, fontsize, align } */ +const WidgetConfig bootstrConf PROGMEM = { 0, 64-8, 1, WA_CENTER }; +const WidgetConfig bitrateConf PROGMEM = { TFT_FRAMEWDT+20, 64-11-10, 1, WA_LEFT }; +//const WidgetConfig voltxtConf PROGMEM = { 32, 108, 1, WA_RIGHT }; +const WidgetConfig iptxtConf PROGMEM = { 0, 64-12, 1, WA_LEFT }; +const WidgetConfig rssiConf PROGMEM = { 0, 64-11-10, 1, WA_LEFT }; +const WidgetConfig numConf PROGMEM = { TFT_FRAMEWDT, 57, 35, WA_CENTER }; +const WidgetConfig apNameConf PROGMEM = { 0, 18, 1, WA_CENTER }; +const WidgetConfig apName2Conf PROGMEM = { 0, 26, 1, WA_CENTER }; +const WidgetConfig apPassConf PROGMEM = { 0, 37, 1, WA_CENTER }; +const WidgetConfig apPass2Conf PROGMEM = { 0, 45, 1, WA_CENTER }; +//const WidgetConfig clockConf PROGMEM = { 6, 34, 2, WA_CENTER }; +const WidgetConfig clockConf PROGMEM = { 0, 57, 35, WA_RIGHT }; /* 35 is a fixed font size. do not change */ +const WidgetConfig vuConf PROGMEM = { 1, 28, 1, WA_LEFT }; + +const WidgetConfig bootWdtConf PROGMEM = { 0, 64-8*2-5, 1, WA_CENTER }; +const ProgressConfig bootPrgConf PROGMEM = { 90, 10, 4 }; + +/* BANDS */ /* { onebandwidth, onebandheight, bandsHspace, bandsVspace, numofbands, fadespeed } */ +const VUBandsConfig bandsConf PROGMEM = { 12, 48, 2, 1, 8, 3 }; + +/* STRINGS */ +const char numtxtFmt[] PROGMEM = "%d"; +const char rssiFmt[] PROGMEM = "%d"; +const char iptxtFmt[] PROGMEM = "%s"; +//const char voltxtFmt[] PROGMEM = "%d"; +const char bitrateFmt[] PROGMEM = "%d kBs"; + +/* MOVES */ /* { left, top, width } */ +const MoveConfig clockMove PROGMEM = { 0, 0, -1 }; +const MoveConfig weatherMove PROGMEM = { 0, 0, -1 }; +const MoveConfig weatherMoveVU PROGMEM = { 0, 0, -1 }; + +#endif diff --git a/yoRadio/src/displays/displaySSD1322.cpp b/yoRadio/src/displays/displaySSD1322.cpp new file mode 100644 index 0000000..2f9b00b --- /dev/null +++ b/yoRadio/src/displays/displaySSD1322.cpp @@ -0,0 +1,220 @@ +#include "../core/options.h" +#if DSP_MODEL==DSP_SSD1322 + +#include "displaySSD1322.h" +#include "../core/player.h" +#include "../core/config.h" +#include "../core/network.h" + +#define LOGO_WIDTH 21 +#define LOGO_HEIGHT 32 + +#define TAKE_MUTEX() if(player.mutex_pl) xSemaphoreTake(player.mutex_pl, portMAX_DELAY) +#define GIVE_MUTEX() if(player.mutex_pl) xSemaphoreGive(player.mutex_pl) + +#ifndef DEF_SPI_FREQ + #define DEF_SPI_FREQ 16000000UL /* set it to 0 for system default */ +#endif + +const unsigned char logo [] PROGMEM= +{ + 0x06, 0x03, 0x00, 0x0f, 0x07, 0x80, 0x1f, 0x8f, 0xc0, 0x1f, 0x8f, 0xc0, + 0x0f, 0x07, 0x80, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x03, 0xff, 0x00, 0x0f, 0xff, 0x80, + 0x1f, 0xff, 0xc0, 0x1f, 0xff, 0xc0, 0x3f, 0x8f, 0xe0, 0x7e, 0x03, 0xf0, + 0x7c, 0x01, 0xf0, 0x7c, 0x01, 0xf0, 0x7f, 0xff, 0xf0, 0xff, 0xff, 0xf8, + 0xff, 0xff, 0xf8, 0xff, 0xff, 0xf8, 0x7c, 0x00, 0x00, 0x7c, 0x00, 0x00, + 0x7e, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x3f, 0xc0, 0xe0, 0x3f, 0xff, 0xe0, + 0x1f, 0xff, 0xe0, 0x0f, 0xff, 0xe0, 0x03, 0xff, 0xc0, 0x00, 0xfe, 0x00 +}; + + +#if DSP_HSPI + DspCore::DspCore(): Jamis_SSD1322(256, 64, &SPI2, TFT_DC, TFT_RST, TFT_CS, DEF_SPI_FREQ) {} +#else + DspCore::DspCore(): Jamis_SSD1322(256, 64, &SPI, TFT_DC, TFT_RST, TFT_CS, DEF_SPI_FREQ) {} +#endif + +#include "tools/utf8RusGFX.h" + +void DspCore::initDisplay() { +#include "tools/oledcolorfix.h" + begin(); + cp437(true); + flip(); + invert(); + setTextWrap(false); + + plItemHeight = playlistConf.widget.textsize*(CHARHEIGHT-1)+playlistConf.widget.textsize*4; + plTtemsCount = round((float)height()/plItemHeight); + if(plTtemsCount%2==0) plTtemsCount++; + plCurrentPos = plTtemsCount/2; + plYStart = (height() / 2 - plItemHeight / 2) - plItemHeight * (plTtemsCount - 1) / 2 + playlistConf.widget.textsize*2; +} + +void DspCore::drawLogo(uint16_t top) { + drawBitmap( (width() - LOGO_WIDTH ) / 2, 8, logo, LOGO_WIDTH, LOGO_HEIGHT, 1); + display(); +} + +void DspCore::printPLitem(uint8_t pos, const char* item, ScrollWidget& current){ + setTextSize(playlistConf.widget.textsize); + if (pos == plCurrentPos) { + current.setText(item); + } else { + uint8_t plColor = (abs(pos - plCurrentPos)-1)>4?4:abs(pos - plCurrentPos)-1; + setTextColor(config.theme.playlist[plColor], config.theme.background); + setCursor(TFT_FRAMEWDT, plYStart + pos * plItemHeight); + fillRect(0, plYStart + pos * plItemHeight - 1, width(), plItemHeight - 2, config.theme.background); + print(utf8Rus(item, true)); + } +} + +void DspCore::drawPlaylist(uint16_t currentItem) { + uint8_t lastPos = config.fillPlMenu(currentItem - plCurrentPos, plTtemsCount); + if(lastPosglyph + c; +} + +uint8_t DspCore::_charWidth(unsigned char c){ + GFXglyph *glyph = pgm_read_glyph_ptr(&DS_DIGI28pt7b, 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(_oldtimeleft, clockTop-clockTimeHeight+1, _oldtimewidth+CHARWIDTH*2+2, clockTimeHeight, config.theme.background); + //if(_oldtimeleft>0) dsp.fillRect(_oldtimeleft, clockTop-clockTimeHeight+1, _oldtimewidth, clockTimeHeight, config.theme.background); + _timeleft = (width()/*/2*/ - _timewidth/*/2*/)-clockRightSpace; + setTextSize(1); + setFont(&DS_DIGI28pt7b); + setTextColor(config.theme.clock, config.theme.background); + setCursor(_timeleft, clockTop); + print(_timeBuf); + setFont(); + strlcpy(_oldTimeBuf, _timeBuf, sizeof(_timeBuf)); + _oldtimewidth = _timewidth; + _oldtimeleft = _timeleft; +} + +void DspCore::printClock(uint16_t top, uint16_t rightspace, uint16_t timeheight, bool redraw){ + clockTop = top; + clockRightSpace = rightspace; + clockTimeHeight = timeheight; + strftime(_timeBuf, sizeof(_timeBuf), "%H:%M", &network.timeinfo); + if(strcmp(_oldTimeBuf, _timeBuf)!=0 || redraw){ + _getTimeBounds(); + _clockTime(); + } + _clockSeconds(); +} + +void DspCore::clearClock(){ + dsp.fillRect(_timeleft, clockTop-clockTimeHeight, _timewidth+2, clockTimeHeight, config.theme.background); + //dsp.fillRect(_timeleft, clockTop-clockTimeHeight, _timewidth+CHARWIDTH*3*2+24, clockTimeHeight+10+CHARHEIGHT, config.theme.background); +} + +void DspCore::startWrite(void) { + //TAKE_MUTEX(); + Jamis_SSD1322::startWrite(); +} + +void DspCore::endWrite(void) { + Jamis_SSD1322::endWrite(); + //GIVE_MUTEX(); +} + +void DspCore::loop(bool force) { + //clearDisplay(); + display(); + delay(5); +} + +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(){ + setRotation(config.store.flipscreen?2:0); +} + +void DspCore::invert(){ + invertDisplay(config.store.invertdisplay); +} + +void DspCore::sleep(void) { oled_command(SSD1322_DISPLAYOFF); } +void DspCore::wake(void) { oled_command(SSD1322_DISPLAYON); } + +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; + } + Jamis_SSD1322::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; + } + Jamis_SSD1322::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_DIGI28pt7b); + setTextSize(1); +} + +#endif diff --git a/yoRadio/src/displays/displaySSD1322.h b/yoRadio/src/displays/displaySSD1322.h new file mode 100644 index 0000000..56fbbf4 --- /dev/null +++ b/yoRadio/src/displays/displaySSD1322.h @@ -0,0 +1,43 @@ +#ifndef displaySSD1305_h +#define displaySSD1305_h +#include "../core/options.h" + +#include "Arduino.h" +#include +#include "../SSD1322/SSD1322.h" +#include "fonts/DS_DIGI28pt7b.h" +#include "tools/l10n.h" + +#define CHARWIDTH 6 +#define CHARHEIGHT 8 + +#define DSP_OLED + +typedef GFXcanvas1 Canvas; +#include "widgets/widgets.h" +#include "widgets/pages.h" + +#if __has_include("conf/displaySSD1305conf_custom.h") + #include "conf/displaySSD1322conf_custom.h" +#else + #include "conf/displaySSD1322conf.h" +#endif + +class DspCore: public Jamis_SSD1322 { +#include "tools/commongfx.h" +}; + +extern DspCore dsp; + +/* + * OLED COLORS + */ +#define BOOT_PRG_COLOR WHITE +#define BOOT_TXT_COLOR WHITE +#define PINK WHITE +#define SILVER WHITE +#define TFT_BG BLACK +#define TFT_FG WHITE +#define TFT_LOGO WHITE + +#endif diff --git a/yoRadio/src/displays/dspcore.h b/yoRadio/src/displays/dspcore.h index 5612839..0ef1317 100644 --- a/yoRadio/src/displays/dspcore.h +++ b/yoRadio/src/displays/dspcore.h @@ -40,6 +40,8 @@ #include "displayGC9A01A.h" #elif DSP_MODEL==DSP_ILI9488 || DSP_MODEL==DSP_ILI9486 #include "displayILI9488.h" +#elif DSP_MODEL==DSP_SSD1322 + #include "displaySSD1322.h" #endif //extern DspCore dsp; diff --git a/yoRadio/src/displays/widgets/widgets.cpp b/yoRadio/src/displays/widgets/widgets.cpp index 8449aed..ba2e432 100644 --- a/yoRadio/src/displays/widgets/widgets.cpp +++ b/yoRadio/src/displays/widgets/widgets.cpp @@ -381,7 +381,7 @@ void NumWidget::setText(const char* txt) { _getBounds(); if (strcmp(_oldtext, _text) == 0) return; uint16_t realth = _textheight; -#ifdef DSP_OLED +#if defined(DSP_OLED) && DSP_MODEL!=DSP_SSD1322 realth = _textheight*CHARHEIGHT; #endif if (_active) dsp.fillRect(_oldleft == 0 ? _realLeft() : min(_oldleft, _realLeft()), _config.top-_textheight+1, max(_oldtextwidth, _textwidth), realth, _bgcolor);