|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915 |
- // All speed gains and original optimized library for the ILI9341 are credit to Paul Stoffregen.
-
- /***************************************************
- This is adapted from the library for the Adafruit ILI9341 display, but for the SSD1351.
- ----> https://www.adafruit.com/products/1673
-
- Check out the links above for our tutorials and wiring diagrams
- These displays use SPI to communicate, 4 or 5 pins are required to
- interface (RST is optional)
- Adafruit invests time and resources providing this open source code,
- please support Adafruit and open-source hardware by purchasing
- products from Adafruit!
-
- Written by Limor Fried/Ladyada for Adafruit Industries.
- MIT license, all text above must be included in any redistribution
- ****************************************************/
-
- #if !defined(TEENSYDUINO) || !defined(__arm__)
- #error "Sorry, this optimized library only works on 32 bit Teensy. Use Adafruit_SSD1351 for others."
- #elif defined(__MKL26Z64__)
- #error "Sorry, this optimized library doesn't work with Teensy LC. Use Adafruit_SSD1351."
- #endif
-
- #pragma once
- #include <Arduino.h>
- #include <array>
- #include <string>
- #include <SPI.h>
- #include "color.h"
- #include "buffer.h"
- #include "gfxfont.h"
- #include "Fonts/all_fonts.h"
-
- extern "C" {
- int _getpid(){ return -1;}
- int _kill(int pid, int sig){ return -1; }
- int _write(){return -1;}
- }
-
- #ifndef swap
- template <typename T> void __attribute__((always_inline)) swap(T &a, T &b) {
- T t = a;
- a = b;
- b = t;
- }
- #endif
-
- // Magical member templating magic to make special members for buffering / non-buffering more readable.
- // Taken from http://lists.boost.org/Archives/boost/2014/08/215954.php
- #define REQUIRES(...) typename std::enable_if<(__VA_ARGS__), int>::type = 0
- #define MEMBER_REQUIRES(...) template<bool HiddenMemberBool=true, REQUIRES(HiddenMemberBool && (__VA_ARGS__))>
-
- namespace ssd1351 {
-
- // Use 18mhz SPI as that seems about the fastest my version of the display can deal with. For some reason this still breaks
- // when overclocking the teensy, insights into this would be highly appreciated.
- // To work around it, you can define SLOW_SPI before including this, in which case the SPI speed is reduced to 15MHz.
- // This slows down the display communication quite a lot - but at least it allows running this on an overclocked teensy.
- #ifndef SPICLOCK
- #ifdef SLOW_SPI
- #define SPICLOCK 15000000
- #else
- #define SPICLOCK 18000000
- #endif
- #endif
-
- #define CMD_COMMAND_LOCK 0xFD
- // These two bytes are used to issue some display lock commands for the init. I don't know what they do, but they seem necessary.
- #define COMMAND_LOCK_INIT1 0x12 // "Unlock OLED driver IC MCU interface from entering command"
- #define COMMAND_LOCK_INIT2 0xB1 // "Command A2,B1,B3,BB,BE accessible if in unlock state"
- #define CMD_DISPLAY_SLEEP 0xAE // Set display to sleep mode
- #define CMD_DISPLAY_WAKE 0xAF // Wake up display from sleep mode
- #define CMD_CLOCK_DIVIDER 0xB3 // Set clock divider and display frequency
- #define CMD_REMAP 0xA0 // Remap various display settings, like hardware mapping and most importantly color mode
- #define CMD_START_LINE 0xA1 // Set display start line, needs to be set to 96 for 128x96 displays
- #define CMD_DISPLAY_OFFSET 0xA2 // Set display offset (hardware dependent, needs to be set to 0)
- #define CMD_FUNCTION_SELECTION 0xAB // Used to activate/deactivate internal voltage regulator
- #define CMD_NORMAL_MODE 0xA6 // Normal display mode (display contents of video RAM)
- #define INTERNAL_VREG 0x01 // internal voltage regulator, other value is never used
- #define CMD_COLUMN_ADDRESS 0x15 // Set start and end column of active video RAM area
- #define CMD_ROW_ADDRESS 0x75 // Set start and end row of active video RAM area
- #define CMD_WRITE_TO_RAM 0x5C // Start writing to the video ram. After this, color data can be sent.
- #define CMD_NOOP 0xAD // Sometimes used as a last command - doesn't do anything.
-
- // Text alignments
- static const uint8_t ALIGN_LEFT = 0;
- static const uint8_t ALIGN_CENTER = 1;
- static const uint8_t ALIGN_RIGHT = 2;
-
- static const uint8_t HIGH_COLOR = 0;
- static const uint8_t LOW_COLOR = 1;
-
- const auto black = RGB();
-
- static const SPISettings spi_settings(SPICLOCK, MSBFIRST, SPI_MODE0);
-
- struct Rect {
- int16_t x;
- int16_t y;
- int16_t w;
- int16_t h;
- };
-
- template <typename C, typename B, int W = 128, int H = 128>
- class SSD1351 : public Print {
- public:
- SSD1351(
- uint8_t _cs = 10,
- uint8_t _dc = 15,
- uint8_t _reset = 14,
- uint8_t _mosi=11,
- uint8_t _sclk=13
- ) : cs(_cs), dc(_dc), reset(_reset), mosi(_mosi), sclk(_sclk) {}
-
- void begin() {
- // Initialize the display. This validates the used pins for hardware SPI use,
- // goes through the displays init routine and sets some important options like
- // color depth and display size.
- // Only size and color depth are settable - everything else is hardcoded.
-
- // verify SPI pins are valid;
- if (SPI.pinIsMOSI(mosi) && SPI.pinIsSCK(sclk)) {
- SPI.setMOSI(mosi);
- SPI.setSCK(sclk);
- } else {
- Serial.println("SPI pins are invalid.");
- return;
- }
-
- SPI.begin();
-
- #ifdef KINETISK
- if (SPI.pinIsChipSelect(cs, dc)) {
- // Configure both cs and dc as chip selects, which allows triggering them extremely fast
- // pcs_data and pcs_command contain the bitmasks used when setting the pin states.
- pcs_data = SPI.setCS(cs);
- pcs_command = pcs_data | SPI.setCS(dc);
- } else {
- Serial.println("CS and DC need to be special chip select pins.");
- pcs_data = 0;
- pcs_command = 0;
- return;
- }
- #elif defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
- _csport = portOutputRegister(cs);
- _cspinmask = digitalPinToBitMask(cs);
- pinMode(cs, OUTPUT);
- DIRECT_WRITE_HIGH(_csport, _cspinmask);
- _spi_tcr_current = IMXRT_LPSPI4_S.TCR; // get the current TCR value
-
- // TODO: Need to setup DC to actually work.
- if (SPI.pinIsChipSelect(dc)) {
- SPI.setCS(dc);
- _dcport = 0;
- _dcpinmask = 0;
- } else {
- _dcport = portOutputRegister(dc);
- _dcpinmask = digitalPinToBitMask(dc);
- pinMode(dc, OUTPUT);
- DIRECT_WRITE_HIGH(_dcport, _dcpinmask);
- }
- maybeUpdateTCR(LPSPI_TCR_PCS(1) | LPSPI_TCR_FRAMESZ(7));
- #endif
- // toggle reset low to reset
- if (reset < 255) {
- pinMode(reset, OUTPUT);
- digitalWrite(reset, HIGH);
- delay(10);
- digitalWrite(reset, LOW);
- delay(10);
- digitalWrite(reset, HIGH);
- delay(10);
- }
- delay(30);
-
- beginSPITransaction();
-
- // Set display command lock settings - they have something to do with when the display can receive which commands,
- // but I don't exactly understand what the implications are.
- sendCommandAndContinue(CMD_COMMAND_LOCK);
- sendDataAndContinue(COMMAND_LOCK_INIT1);
- sendCommandAndContinue(CMD_COMMAND_LOCK);
- sendDataAndContinue(COMMAND_LOCK_INIT2);
-
- sendCommandAndContinue(CMD_DISPLAY_SLEEP);
-
- sendCommandAndContinue(CMD_CLOCK_DIVIDER);
- // First 4 bits (1111) are the display frequency (highest), last 4 bits (0001) are the clock divider (lowest)
- sendDataAndContinue(0xF1);
-
- // Set various mapping settings (0x74 = 0111 0100 for low color mode and 0xB4 (1011 0100) for high color mode)
-
- // 01: color mode (01 is 64k colors, 10 is 262k colors)
- // 1: COM pins split (hardware dependent)
- // 1: COM Scan direction up to down (hardware dependent)
-
- // 0: Reserved
- // 1: Colour sequence C -> B -> A instead of A -> B -> C
- // 0: Column layout mapping (hardware dependent)
- // 0: Horizontal address increment mode (x is increased after each write and wraps)
- setColorDepth();
-
- // Set start line - this needs to be 0 for a 128x128 display and 96 for a 128x96 display
- sendCommandAndContinue(CMD_START_LINE);
- sendDataAndContinue(H == 128 ? 0 : 96);
-
- // Set display offset - this is always zero
- sendCommandAndContinue(CMD_DISPLAY_OFFSET);
- sendDataAndContinue(0);
-
- // Select the internal voltage regulator
- sendCommandAndContinue(CMD_FUNCTION_SELECTION);
- sendDataAndContinue(INTERNAL_VREG);
-
- // Set display to normal operation and leave sleep mode
- sendCommandAndContinue(CMD_NORMAL_MODE);
- sendLastCommand(CMD_DISPLAY_WAKE);
-
- endSPITransaction();
- }
-
- void sleep(bool enable) {
- beginSPITransaction();
- sendLastCommand(enable ? CMD_DISPLAY_SLEEP : CMD_DISPLAY_WAKE);
- endSPITransaction();
- }
-
- void drawRect(int16_t x, int16_t y, int16_t w, int16_t h, const C &color) {
- drawFastHLine(x, y, w, color);
- drawFastHLine(x, y + h - 1, w, color);
- drawFastVLine(x, y, h, color);
- drawFastVLine(x + w - 1, y, h, color);
- }
-
- void drawCircle(int16_t x0, int16_t y0, int16_t r, const C &color) {
- int16_t f = 1 - r;
- int16_t ddF_x = 1;
- int16_t ddF_y = -2 * r;
- int16_t x = 0;
- int16_t y = r;
-
- drawPixel(x0 , y0+r, color);
- drawPixel(x0 , y0-r, color);
- drawPixel(x0+r, y0 , color);
- drawPixel(x0-r, y0 , color);
-
- while (x<y) {
- if (f >= 0) {
- y--;
- ddF_y += 2;
- f += ddF_y;
- }
- x++;
- ddF_x += 2;
- f += ddF_x;
-
- drawPixel(x0 + x, y0 + y, color);
- drawPixel(x0 - x, y0 + y, color);
- drawPixel(x0 + x, y0 - y, color);
- drawPixel(x0 - x, y0 - y, color);
- drawPixel(x0 + y, y0 + x, color);
- drawPixel(x0 - y, y0 + x, color);
- drawPixel(x0 + y, y0 - x, color);
- drawPixel(x0 - y, y0 - x, color);
- }
- }
-
- void drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, const C &color) {
- int16_t f = 1 - r;
- int16_t ddF_x = 1;
- int16_t ddF_y = -2 * r;
- int16_t x = 0;
- int16_t y = r;
-
- while (x<y) {
- if (f >= 0) {
- y--;
- ddF_y += 2;
- f += ddF_y;
- }
- x++;
- ddF_x += 2;
- f += ddF_x;
- if (cornername & 0x4) {
- drawPixel(x0 + x, y0 + y, color);
- drawPixel(x0 + y, y0 + x, color);
- }
- if (cornername & 0x2) {
- drawPixel(x0 + x, y0 - y, color);
- drawPixel(x0 + y, y0 - x, color);
- }
- if (cornername & 0x8) {
- drawPixel(x0 - y, y0 + x, color);
- drawPixel(x0 - x, y0 + y, color);
- }
- if (cornername & 0x1) {
- drawPixel(x0 - y, y0 - x, color);
- drawPixel(x0 - x, y0 - y, color);
- }
- }
- }
-
- void fillCircle(int16_t x0, int16_t y0, int16_t r, const C &color) {
- drawFastVLine(x0, y0-r, 2*r+1, color);
- fillCircleHelper(x0, y0, r, 3, 0, color);
- }
-
- void fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, const C &color) {
- int16_t f = 1 - r;
- int16_t ddF_x = 1;
- int16_t ddF_y = -2 * r;
- int16_t x = 0;
- int16_t y = r;
-
- while (x<y) {
- if (f >= 0) {
- y--;
- ddF_y += 2;
- f += ddF_y;
- }
- x++;
- ddF_x += 2;
- f += ddF_x;
-
- if (cornername & 0x1) {
- drawFastVLine(x0+x, y0-y, 2*y+1+delta, color);
- drawFastVLine(x0+y, y0-x, 2*x+1+delta, color);
- }
- if (cornername & 0x2) {
- drawFastVLine(x0-x, y0-y, 2*y+1+delta, color);
- drawFastVLine(x0-y, y0-x, 2*x+1+delta, color);
- }
- }
- }
-
- void drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, const C &color) {
- drawLine(x0, y0, x1, y1, color);
- drawLine(x1, y1, x2, y2, color);
- drawLine(x2, y2, x0, y0, color);
- }
-
- void fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, const C &color) {
- int16_t a, b, y, last;
-
- // Sort coordinates by Y order (y2 >= y1 >= y0)
- if (y0 > y1) {
- swap(y0, y1); swap(x0, x1);
- }
- if (y1 > y2) {
- swap(y2, y1); swap(x2, x1);
- }
- if (y0 > y1) {
- swap(y0, y1); swap(x0, x1);
- }
-
- if(y0 == y2) { // Handle awkward all-on-same-line case as its own thing
- a = b = x0;
- if(x1 < a) a = x1;
- else if(x1 > b) b = x1;
- if(x2 < a) a = x2;
- else if(x2 > b) b = x2;
- drawFastHLine(a, y0, b-a+1, color);
- return;
- }
-
- int16_t
- dx01 = x1 - x0,
- dy01 = y1 - y0,
- dx02 = x2 - x0,
- dy02 = y2 - y0,
- dx12 = x2 - x1,
- dy12 = y2 - y1,
- sa = 0,
- sb = 0;
-
- // For upper part of triangle, find scanline crossings for segments
- // 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1
- // is included here (and second loop will be skipped, avoiding a /0
- // error there), otherwise scanline y1 is skipped here and handled
- // in the second loop...which also avoids a /0 error here if y0=y1
- // (flat-topped triangle).
- if(y1 == y2) {
- last = y1; // Include y1 scanline
- } else {
- last = y1-1; // Skip it
- }
-
- for(y = y0; y <= last; y++) {
- a = x0 + sa / dy01;
- b = x0 + sb / dy02;
- sa += dx01;
- sb += dx02;
- /* longhand:
- a = x0 + (x1 - x0) * (y - y0) / (y1 - y0);
- b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
- */
- if(a > b) {
- swap(a,b);
- }
- drawFastHLine(a, y, b - a + 1, color);
- }
-
- // For lower part of triangle, find scanline crossings for segments
- // 0-2 and 1-2. This loop is skipped if y1=y2.
- sa = dx12 * (y - y1);
- sb = dx02 * (y - y0);
- for(; y <= y2; y++) {
- a = x1 + sa / dy12;
- b = x0 + sb / dy02;
- sa += dx12;
- sb += dx02;
- /* longhand:
- a = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
- b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
- */
- if(a > b) {
- swap(a,b);
- }
- drawFastHLine(a, y, b-a+1, color);
- }
- }
-
- void drawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, const C &color) {
- drawFastHLine(x + r, y, w - 2 * r, color); // Top
- drawFastHLine(x + r, y + h - 1, w - 2 * r, color); // Bottom
- drawFastVLine(x, y + r , h - 2 * r, color); // Left
- drawFastVLine(x + w - 1, y + r, h - 2 * r, color); // Right
- // draw four corners
- drawCircleHelper(x + r, y + r, r, 1, color);
- drawCircleHelper(x + w - r - 1, y + r, r, 2, color);
- drawCircleHelper(x + w - r - 1, y + h - r - 1, r, 4, color);
- drawCircleHelper(x + r, y + h - r - 1, r, 8, color);
- }
-
- void fillRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, const C &color) {
- fillRect(x + r, y, w - 2 * r, h, color);
-
- // draw four corners
- fillCircleHelper(x + w - r - 1, y + r, r, 1, h - 2 * r - 1, color);
- fillCircleHelper(x + r, y + r, r, 2, h - 2 * r - 1, color);
- }
-
- void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, const C &color) {
- int16_t i, j, byteWidth = (w + 7) / 8;
-
- for(j = 0; j < h; j++) {
- for(i = 0; i < w; i++) {
- if(pgm_read_byte(bitmap + j * byteWidth + i / 8) & (128 >> (i & 7))) {
- drawPixel(x + i, y + j, color);
- }
- }
- }
- }
-
- static int16_t getWidth(void) { return W; }
- static int16_t getHeight(void) { return H; }
-
- // Text methods
- void setCursor(int16_t x, int16_t y) {
- cursor_x = x;
- line_start_x = x;
- cursor_y = y;
- }
-
- // get current cursor position (get rotation safe maximum values, using: width() for x, height() for y)
- int16_t getCursorX() const {
- return cursor_x;
- }
-
- int16_t getCursorY() const {
- return cursor_y;
- }
-
- void setTextColor(C color) {
- // Setting just a text color implies a transparent background, which is implied
- // by using the same color for background and foreground.
- text_color = text_bg_color = color;
- }
- void setTextColor(const C &foreground, const C &background) {
- text_color = foreground;
- text_bg_color = background;
- }
-
- void setTextSize(uint8_t new_size) {
- text_size = (new_size > 0) ? new_size : 1;
- }
-
- void setTextWrap(boolean _wrap) {
- wrap = _wrap;
- }
-
- void cp437(bool use_cp437 = true) {
- cp437 = use_cp437;
- }
-
- void setFont(const GFXfont &new_font) {
- font = (GFXfont *)&new_font;
- }
-
- void drawText(const char *str, int16_t x, int16_t y, uint8_t align=ALIGN_LEFT) {
- uint8_t string_length = strlen(str);
-
- Rect bounds = getTextBounds(str, x, y);
-
- // Store previous settings for cursor and wrapping
- uint8_t prev_cursor_x = cursor_x;
- uint8_t prev_cursor_y = cursor_y;
- bool prevWrap = wrap;
-
-
- // Set cursor and disable wrapping
- switch(align) {
- case ALIGN_LEFT:
- cursor_x = x;
- break;
- case ALIGN_CENTER:
- cursor_x = x - bounds.w / 2 - (bounds.x - x);
- break;
- case ALIGN_RIGHT:
- cursor_x = x - bounds.w;
- break;
- }
- cursor_y = y;
- wrap = false;
-
- print(str);
-
- // Restore previous cursor and wrap setting
- cursor_x = prev_cursor_x;
- cursor_y = prev_cursor_y;
- wrap = prevWrap;
- }
-
- uint16_t getTextWidth(const char *str) {
- return getTextBounds(str, 0, 0).w;
- }
-
- // Pass string and a cursor position, returns UL corner and W,H.
- Rect getTextBounds(const char *str, int16_t x, int16_t y) {
- uint8_t c; // Current character
- GFXglyph *glyph;
- uint8_t first = font->first;
- uint8_t last = font->last;
- int16_t min_x = W;
- int16_t min_y = H;
- int16_t max_x = -1;
- int16_t max_y = -1;
-
- // Bounding box coordinates for the current glyph
- int16_t glyph_x1 = 0;
- int16_t glyph_y1 = 0;
- int16_t glyph_x2 = 0;
- int16_t glyph_y2 = 0;
-
- while((c = *str++)) {
- if(c != '\n') { // Not a newline
- if((c == '\r') || (c < font->first) || (c > font->last)) {
- // Char not present in current font
- continue;
- }
-
- c -= font->first;
- glyph = &(font->glyph[c]);
-
- if(wrap && (x + (glyph->xOffset + glyph->width) * text_size >= W)) {
- // Line wrap
- x = line_start_x; // Reset x to 0
- y += font->yAdvance; // Advance y by 1 line
- }
- glyph_x1 = x + glyph->xOffset * text_size;
- glyph_y1 = y + glyph->yOffset * text_size;
- glyph_x2 = glyph_x1 + glyph->width * text_size - 1;
- glyph_y2 = glyph_y1 + glyph->height * text_size - 1;
- if(glyph_x1 < min_x) {
- min_x = glyph_x1;
- }
- if(glyph_y1 < min_y) {
- min_y = glyph_y1;
- }
- if(glyph_x2 > max_x) {
- max_x = glyph_x2;
- }
- if(glyph_y2 > max_y) {
- max_y = glyph_y2;
- }
-
- if ((font != &TomThumb) || *(str + 1)) {
- x += glyph->xAdvance * text_size;
- }
- } else { // Newline
- x = line_start_x; // Reset x
- y += font->yAdvance; // Advance y by 1 line
- }
- }
-
- return {min_x, min_y, max_x - min_x + 1, max_y - min_y + 1};
- }
-
- size_t write(uint8_t c) {
- if(!font) {
- return 1;
- }
-
- if(c == '\n') {
- cursor_x = line_start_x;
- cursor_y += (int16_t)text_size * (uint8_t)font->yAdvance;
- } else if(c != '\r') {
- uint8_t first = font->first;
- if((c >= first) && (c <= font->last)) {
- uint8_t c2 = c - font->first;
- GFXglyph *glyph = &(font->glyph[c2]);
- uint8_t w = glyph->width;
- uint8_t h = glyph->height;
- if((w > 0) && (h > 0)) { // Is there an associated bitmap?
- int16_t xo = glyph->xOffset; // sic
- if(wrap && ((cursor_x + text_size * (xo + w)) >= W)) {
- // Drawing character would go off right edge; wrap to new line
- cursor_x = line_start_x;
- cursor_y += (int16_t)text_size * font->yAdvance;
- }
- drawChar(cursor_x, cursor_y, c, text_color, text_bg_color, text_size);
- }
- cursor_x += glyph->xAdvance * (int16_t)text_size;
- }
- }
-
- return 1;
- }
-
- void drawChar(int16_t x, int16_t y, unsigned char c, C color, C bg, uint8_t size) {
- if (!font) {
- return;
- }
- // Character is assumed previously filtered by write() to eliminate
- // newlines, returns, non-printable characters, etc. Calling drawChar()
- // directly with 'bad' characters of font may cause mayhem!
-
- c -= font->first;
- GFXglyph *glyph = &(font->glyph[c]);
- uint8_t *bitmap = font->bitmap;
-
- uint16_t bo = glyph->bitmapOffset;
- uint8_t w = glyph->width;
- uint8_t h = glyph->height;
- int8_t xo = glyph->xOffset;
- int8_t yo = glyph->yOffset;
- uint8_t xx = 0;
- uint8_t yy = 0;
- uint8_t bits = 0;
- uint8_t bit = 0;
- int16_t xo16 = 0;
- int16_t yo16 = 0;
-
- if(size > 1) {
- xo16 = xo;
- yo16 = yo;
- }
-
- for(yy=0; yy<h; yy++) {
- for(xx=0; xx<w; xx++) {
- if(!(bit++ & 7)) {
- bits = bitmap[bo++];
- }
- if(bits & 0x80) {
- if(size == 1) {
- drawPixel(x+xo+xx, y+yo+yy, color);
- } else {
- fillRect(x+(xo16+xx)*size, y+(yo16+yy)*size, size, size, color);
- }
- }
- bits <<= 1;
- }
- }
- }
-
- void sendCommand(uint8_t commandByte, const uint8_t *dataBytes, uint8_t numDataBytes) {
- beginSPITransaction();
- if (numDataBytes) {
- sendCommandAndContinue(commandByte);
- for (uint8_t i=0; i<(numDataBytes-1); i++) {
- sendDataAndContinue(*dataBytes++); // Send the data bytes
- }
- sendLastData(*dataBytes);
- } else {
- sendLastCommand(commandByte);
- }
-
- endSPITransaction();
-
- }
-
- // Yeah, this is somewhere between silly and crazy.
- // Suggestions on how to include the implementations that work without getting rid of MEMBER_REQUIRES are more than welcome.
- #include "ssd1351_highcolor.inl"
- #include "ssd1351_lowcolor.inl"
- #include "ssd1351_indexedcolor.inl"
-
- #include "ssd1351_nobuffer.inl"
- #include "ssd1351_singlebuffer.inl"
-
- private:
- typedef std::array<C, W * H> ArrayType;
-
- MEMBER_REQUIRES(std::is_same<B, SingleBuffer>::value)
- __attribute__((always_inline)) ArrayType& frontBuffer() {
- static ArrayType buffer;
- return buffer;
- }
-
- // Pins
- uint8_t cs;
- uint8_t dc;
- uint8_t reset;
- uint8_t mosi;
- uint8_t sclk;
-
- // Magical registers (I think?) to make toggling DC pin super fast.
- uint8_t pcs_data, pcs_command;
-
- int16_t cursor_x;
- int16_t cursor_y;
- int16_t line_start_x;
- C text_color = black;
- C text_bg_color = black;
- uint8_t text_size = 1;
- bool wrap = true; // If set, 'wrap' text at right edge of display
- bool _cp437 = false; // If set, use correct CP437 charset (default is off)
- GFXfont *font = (GFXfont *)&TomThumb;
- // Teensy 3.x processors
- void __attribute__((always_inline)) setVideoRamPosition(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) {
- // Sets the active video RAM area of the display. After sending this command
- // (and sending the 'write to ram' command), color data can be sent do the display without
- // having to set the x/y address for each pixel. After each pixel, the display will internally
- // increment to point to the next pixel:
- // x0,y0 -> x0+1, y0, ..., x1,y0, x0,y0+1, x0+1,y0+1, ..., x1,y1
-
- sendCommandAndContinue(CMD_COLUMN_ADDRESS);
- sendDataAndContinue(x0);
- sendDataAndContinue(x1);
- sendCommandAndContinue(CMD_ROW_ADDRESS);
- sendDataAndContinue(y0);
- sendDataAndContinue(y1);
- }
-
- // ****
- // Low-level data pushing functions
- // ****
- #ifdef KINETISK
- void beginSPITransaction() __attribute__((always_inline)) {
- SPI.beginTransaction(spi_settings);
- }
- void endSPITransaction() __attribute__((always_inline)) {
- SPI.endTransaction();
- }
- void __attribute__((always_inline)) waitFifoNotFull() {
- uint32_t sr;
- uint32_t tmp __attribute__((unused));
- do {
- sr = KINETISK_SPI0.SR;
- if (sr & 0xF0) tmp = KINETISK_SPI0.POPR; // drain RX FIFO
- } while ((sr & (15 << 12)) > (3 << 12));
- }
-
- void __attribute__((always_inline)) waitTransmitComplete(uint32_t mcr) {
- uint32_t tmp __attribute__((unused));
- while (1) {
- uint32_t sr = KINETISK_SPI0.SR;
- if (sr & SPI_SR_EOQF) break; // wait for last transmit
- if (sr & 0xF0) tmp = KINETISK_SPI0.POPR;
- }
- KINETISK_SPI0.SR = SPI_SR_EOQF;
- SPI0_MCR = mcr;
- while (KINETISK_SPI0.SR & 0xF0) {
- tmp = KINETISK_SPI0.POPR;
- }
- }
-
- void __attribute__((always_inline)) sendCommandAndContinue(uint8_t command) {
- KINETISK_SPI0.PUSHR = command | (pcs_command << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;
- waitFifoNotFull();
- }
- void __attribute__((always_inline)) sendLastCommand(uint8_t command) {
- uint32_t mcr = SPI0_MCR;
- KINETISK_SPI0.PUSHR = command | (pcs_command << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_EOQ;
- waitTransmitComplete(mcr);
- }
-
- void __attribute__((always_inline)) sendDataAndContinue(uint8_t data) {
- KINETISK_SPI0.PUSHR = data | (pcs_data << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;
- waitFifoNotFull();
- }
- void __attribute__((always_inline)) sendLastData(uint8_t data) {
- uint32_t mcr = SPI0_MCR;
- KINETISK_SPI0.PUSHR = data | (pcs_data << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_EOQ;
- waitTransmitComplete(mcr);
- }
- void __attribute__((always_inline)) sendDataAndContinue16(uint16_t data) {
- KINETISK_SPI0.PUSHR = data | (pcs_data << 16) | SPI_PUSHR_CTAS(1) | SPI_PUSHR_CONT;
- waitFifoNotFull();
- }
- void __attribute__((always_inline)) sendLastData16(uint16_t data) {
- uint32_t mcr = SPI0_MCR;
- KINETISK_SPI0.PUSHR = data | (pcs_data << 16) | SPI_PUSHR_CTAS(1) | SPI_PUSHR_EOQ;
- waitTransmitComplete(mcr);
- }
- #elif defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
- uint32_t _cspinmask;
- volatile uint32_t *_csport;
- uint32_t _spi_tcr_current;
- uint32_t _dcpinmask;
- uint8_t _pending_rx_count = 0;
- volatile uint32_t *_dcport;
-
- void __attribute__((always_inline)) DIRECT_WRITE_LOW(volatile uint32_t * base, uint32_t mask) __attribute__((always_inline)) {
- *(base+34) = mask;
- }
- void __attribute__((always_inline)) DIRECT_WRITE_HIGH(volatile uint32_t * base, uint32_t mask) __attribute__((always_inline)) {
- *(base+33) = mask;
- }
- void __attribute__((always_inline)) waitFifoNotFull() {
- uint32_t tmp __attribute__((unused));
- do {
- if ((IMXRT_LPSPI4_S.RSR & LPSPI_RSR_RXEMPTY) == 0) {
- tmp = IMXRT_LPSPI4_S.RDR; // Read any pending RX bytes in
- if (_pending_rx_count) _pending_rx_count--; //decrement count of bytes still levt
- }
- } while ((IMXRT_LPSPI4_S.SR & LPSPI_SR_TDF) == 0) ;
- }
-
- void __attribute__((always_inline)) waitTransmitComplete() {
- uint32_t tmp __attribute__((unused));
- while (_pending_rx_count) {
- if ((IMXRT_LPSPI4_S.RSR & LPSPI_RSR_RXEMPTY) == 0) {
- tmp = IMXRT_LPSPI4_S.RDR; // Read any pending RX bytes in
- _pending_rx_count--; //decrement count of bytes still levt
- }
- }
- IMXRT_LPSPI4_S.CR = LPSPI_CR_MEN | LPSPI_CR_RRF; // Clear RX FIFO
- }
- void __attribute__((always_inline)) waitTransmitComplete(uint32_t mcr) {
- waitTransmitComplete();
- }
- #define TCR_MASK (LPSPI_TCR_PCS(3) | LPSPI_TCR_FRAMESZ(31) | LPSPI_TCR_CONT | LPSPI_TCR_RXMSK )
- void maybeUpdateTCR(uint32_t requested_tcr_state) /*__attribute__((always_inline)) */ {
- if ((_spi_tcr_current & TCR_MASK) != requested_tcr_state) {
- bool dc_state_change = (_spi_tcr_current & LPSPI_TCR_PCS(3)) != (requested_tcr_state & LPSPI_TCR_PCS(3));
- _spi_tcr_current = (_spi_tcr_current & ~TCR_MASK) | requested_tcr_state ;
- // only output when Transfer queue is empty.
- if (!dc_state_change || !_dcpinmask) {
- while ((IMXRT_LPSPI4_S.FSR & 0x1f) ) ;
- IMXRT_LPSPI4_S.TCR = _spi_tcr_current; // update the TCR
-
- } else {
- waitTransmitComplete();
- if (requested_tcr_state & LPSPI_TCR_PCS(3)) DIRECT_WRITE_HIGH(_dcport, _dcpinmask);
- else DIRECT_WRITE_LOW(_dcport, _dcpinmask);
- IMXRT_LPSPI4_S.TCR = _spi_tcr_current & ~(LPSPI_TCR_PCS(3) | LPSPI_TCR_CONT); // go ahead and update TCR anyway?
-
- }
- }
- }
-
- void beginSPITransaction() __attribute__((always_inline)) {
- SPI.beginTransaction(spi_settings);
- if (_csport) {
- DIRECT_WRITE_LOW(_csport, _cspinmask);
- }
- }
- void endSPITransaction() __attribute__((always_inline)) {
- if (_csport)
- DIRECT_WRITE_HIGH(_csport, _cspinmask);
- SPI.endTransaction();
- }
-
- void __attribute__((always_inline)) sendCommandAndContinue(uint8_t command) {
- maybeUpdateTCR(LPSPI_TCR_PCS(0) | LPSPI_TCR_FRAMESZ(7) /*| LPSPI_TCR_CONT*/);
- IMXRT_LPSPI4_S.TDR = command;
- _pending_rx_count++; //
- waitFifoNotFull();
- }
- void __attribute__((always_inline)) sendLastCommand(uint8_t command) {
- maybeUpdateTCR(LPSPI_TCR_PCS(0) | LPSPI_TCR_FRAMESZ(7) /*| LPSPI_TCR_CONT*/);
- IMXRT_LPSPI4_S.TDR = command;
- _pending_rx_count++; //
- waitTransmitComplete();
- }
-
- void __attribute__((always_inline)) sendDataAndContinue(uint8_t data) {
- maybeUpdateTCR(LPSPI_TCR_PCS(1) | LPSPI_TCR_FRAMESZ(7) | LPSPI_TCR_CONT);
- IMXRT_LPSPI4_S.TDR = data;
- _pending_rx_count++; //
- waitFifoNotFull();
- }
- void __attribute__((always_inline)) sendLastData(uint8_t data) {
- maybeUpdateTCR(LPSPI_TCR_PCS(1) | LPSPI_TCR_FRAMESZ(7));
- IMXRT_LPSPI4_S.TDR = data;
- _pending_rx_count++; //
- waitTransmitComplete();
- }
- void __attribute__((always_inline)) sendDataAndContinue16(uint16_t data) {
- maybeUpdateTCR(LPSPI_TCR_PCS(1) | LPSPI_TCR_FRAMESZ(15) | LPSPI_TCR_CONT);
- IMXRT_LPSPI4_S.TDR = data;
- _pending_rx_count++; //
- waitFifoNotFull();
- }
- void __attribute__((always_inline)) sendLastData16(uint16_t data) {
- maybeUpdateTCR(LPSPI_TCR_PCS(1) | LPSPI_TCR_FRAMESZ(15));
- IMXRT_LPSPI4_S.TDR = data;
- _pending_rx_count++; //
- waitTransmitComplete();
- }
- #endif
- };
-
- }
|