From 36a0fa7c240f3a68797ef8ad1ddd07f8b533dcc5 Mon Sep 17 00:00:00 2001 From: Dirk Petrautzki Date: Thu, 22 Oct 2020 13:32:05 +0200 Subject: [PATCH 1/6] Move SNES controller pins because they are needed for spi communication and add further display constants --- constants.h | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/constants.h b/constants.h index 4c59e60..840712d 100644 --- a/constants.h +++ b/constants.h @@ -11,10 +11,10 @@ // SNES Controller // uncomment following line to enable snes controller support -// #define SNES_CONTROLLER -constexpr uint8_t DATA_CLOCK = 11; -constexpr uint8_t DATA_LATCH = 12; -constexpr uint8_t DATA_SERIAL = 13; + #define SNES_CONTROLLER +constexpr uint8_t DATA_CLOCK = 2; +constexpr uint8_t DATA_LATCH = 3; +constexpr uint8_t DATA_SERIAL = 4; // Sound constexpr uint8_t SOUND_PIN = 9; // do not change, belongs to used timer @@ -77,5 +77,13 @@ constexpr uint8_t SCREEN_HEIGHT = 64; constexpr uint8_t HALF_WIDTH = SCREEN_WIDTH/2; constexpr uint8_t RENDER_HEIGHT = 56; // raycaster working height (the rest is for the hud) constexpr uint8_t HALF_HEIGHT = SCREEN_HEIGHT/2; +constexpr uint8_t dcPin = 5; +constexpr uint8_t csPin = 10; +constexpr uint8_t I2C_ADDR = ((SCREEN_HEIGHT == 32) ? 0x3C : 0x3D); // (0x3C for 32-pixel-tall displays, 0x3D for all others). +enum DisplayMode { + I2C_MODE, + SPI_MODE +}; +constexpr DisplayMode MODE = SPI_MODE; #endif From fc78fa0b96a665c7edd4deb76357bec85a390e5c Mon Sep 17 00:00:00 2001 From: Dirk Petrautzki Date: Thu, 22 Oct 2020 13:36:32 +0200 Subject: [PATCH 2/6] Move some one-liners to the header to promote inlining --- SSD1306.cpp | 42 ------------------------------------------ SSD1306.h | 34 +++++++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 45 deletions(-) diff --git a/SSD1306.cpp b/SSD1306.cpp index 93d6d93..a9387ef 100644 --- a/SSD1306.cpp +++ b/SSD1306.cpp @@ -227,18 +227,6 @@ void Adafruit_SSD1306::drawPixel(int16_t x, int16_t y, uint16_t c } } -/*! - @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. -*/ -template -void Adafruit_SSD1306::clearDisplay(void) { - memset(buffer, 0, WIDTH * ((HEIGHT + 7) / 8)); -} - /*! @brief Draw a vertical line. This is also invoked by the Adafruit_GFX library in generating many higher-level graphics primitives. @@ -388,16 +376,6 @@ bool Adafruit_SSD1306::getPixel(int16_t x, int16_t y) { 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. -*/ -template -uint8_t *Adafruit_SSD1306::getBuffer(void) { - return buffer; -} - // REFRESH DISPLAY --------------------------------------------------------- /*! @@ -430,23 +408,3 @@ void Adafruit_SSD1306::display(void) { TWI_Start_Transceiver_With_Data(cmd, ptr, count); } } - -// OTHER HARDWARE SETTINGS ------------------------------------------------- - -/*! - @brief Enable or disable display invert mode (white-on-black vs - black-on-white). - @param i - If true, switch to invert mode (black-on-white), else normal - mode (white-on-black). - @return None (void). - @note This has an immediate effect on the display, no need to call the - display() function -- buffer contents are not changed, rather a - different pixel mode of the display hardware is used. When - enabled, drawing SSD1306_BLACK (value 0) pixels will actually draw white, - SSD1306_WHITE (value 1) will draw black. -*/ -template -void Adafruit_SSD1306::invertDisplay(bool i) { - ssd1306_command1(i ? SSD1306_INVERTDISPLAY : SSD1306_NORMALDISPLAY); -} diff --git a/SSD1306.h b/SSD1306.h index 5c641e0..dfcbe26 100644 --- a/SSD1306.h +++ b/SSD1306.h @@ -91,12 +91,40 @@ class Adafruit_SSD1306 { bool begin(uint8_t switchvcc=SSD1306_SWITCHCAPVCC, uint8_t i2caddr=0); void display(void); - void clearDisplay(void); - void invertDisplay(bool i); void drawPixel(int16_t x, int16_t y, uint16_t color); void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); bool getPixel(int16_t x, int16_t y); - uint8_t *getBuffer(void); + + /*! + @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 clearDisplay(void) { memset(buffer, 0, WIDTH * ((HEIGHT + 7) / 8)); } + + /*! + @brief Enable or disable display invert mode (white-on-black vs + black-on-white). + @param i + If true, switch to invert mode (black-on-white), else normal + mode (white-on-black). + @return None (void). + @note This has an immediate effect on the display, no need to call the + display() function -- buffer contents are not changed, rather a + different pixel mode of the display hardware is used. When + enabled, drawing SSD1306_BLACK (value 0) pixels will actually draw white, + SSD1306_WHITE (value 1) will draw black. + */ + void invertDisplay(bool i) { ssd1306_command1(i ? SSD1306_INVERTDISPLAY : SSD1306_NORMALDISPLAY); } + + /*! + @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 *getBuffer(void) { return buffer; } void clearRect(uint8_t, uint8_t, uint8_t, uint8_t); void drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, int16_t h, uint16_t color); From 398bbf4714c8f312f934741402aff37f61e9ef67 Mon Sep 17 00:00:00 2001 From: Dirk Petrautzki Date: Thu, 22 Oct 2020 13:40:40 +0200 Subject: [PATCH 3/6] Remove OPTIMIZE_SSD1306 define --- constants.h | 1 - display.h | 24 ------------------------ 2 files changed, 25 deletions(-) diff --git a/constants.h b/constants.h index 840712d..a0cb0db 100644 --- a/constants.h +++ b/constants.h @@ -20,7 +20,6 @@ constexpr uint8_t DATA_SERIAL = 4; constexpr uint8_t SOUND_PIN = 9; // do not change, belongs to used timer // GFX settings -#define OPTIMIZE_SSD1306 // Optimizations for SSD1366 displays #define FRAME_TIME 66.666666 // Desired time per frame in ms (66.666666 is ~15 fps) #define RES_DIVIDER 2 // Higher values will result in lower horizontal resolution when rasterize and lower process and memory usage diff --git a/display.h b/display.h index 4b722cb..400bb1f 100644 --- a/display.h +++ b/display.h @@ -31,10 +31,8 @@ Adafruit_SSD1306 display; double delta = 1; uint32_t lastFrameTime = 0; -#ifdef OPTIMIZE_SSD1306 // Optimizations for SSD1306 handles buffer directly uint8_t *display_buf; -#endif // We don't handle more than MAX_RENDER_DEPTH depth, so we can safety store // z values in a byte with 1 decimal and save some memory, @@ -48,9 +46,7 @@ void setupDisplay() { while (1); // Don't proceed, loop forever } -#ifdef OPTIMIZE_SSD1306 display_buf = display.getBuffer(); -#endif // initialize z buffer memset(zbuffer, 0xFF, ZBUFFER_SIZE); @@ -70,9 +66,7 @@ double getActualFps() { // Faster way to render vertical bits void drawByte(uint8_t x, uint8_t y, uint8_t b) { -#ifdef OPTIMIZE_SSD1306 display_buf[(y / 8)*SCREEN_WIDTH + x] = b; -#endif } boolean getGradientPixel(uint8_t x, uint8_t y, uint8_t i) { @@ -104,7 +98,6 @@ void drawPixel(int8_t x, int8_t y, bool color, bool raycasterViewport = false) { return; } -#ifdef OPTIMIZE_SSD1306 if (color) { // white display_buf[x + (y / 8)*SCREEN_WIDTH] |= (1 << (y & 7)); @@ -112,9 +105,6 @@ void drawPixel(int8_t x, int8_t y, bool color, bool raycasterViewport = false) { // black display_buf[x + (y / 8)*SCREEN_WIDTH] &= ~(1 << (y & 7)); } -#else - display.drawPixel(x, y, color); -#endif } // For raycaster only @@ -126,7 +116,6 @@ void drawVLine(uint8_t x, int8_t start_y, int8_t end_y, uint8_t intensity) { int8_t higher_y = min(max(start_y, end_y), RENDER_HEIGHT - 1); uint8_t c; -#ifdef OPTIMIZE_SSD1306 uint8_t bp; uint8_t b; for (c = 0; c < RES_DIVIDER; c++) { @@ -150,19 +139,6 @@ void drawVLine(uint8_t x, int8_t start_y, int8_t end_y, uint8_t intensity) { drawByte(x + c, y - 1, b); } } -#else - y = lower_y; - while (y <= higher_y) { - for (c = 0; c < RES_DIVIDER; c++) { - // bypass black pixels - if (getGradientPixel(x + c, y, intensity)) { - drawPixel(x + c, y, 1, true); - } - } - - y++; - } -#endif } // Custom drawBitmap method with scale support, mask, zindex and pattern filling From 22de7f1503f2391573759c9159b393256e05b849 Mon Sep 17 00:00:00 2001 From: Dirk Petrautzki Date: Thu, 22 Oct 2020 14:03:54 +0200 Subject: [PATCH 4/6] Move drawPixel and drawByte to SSD1306 and remove direct buffer access --- SSD1306.cpp | 41 ----------------------------------------- SSD1306.h | 11 ++++++++--- display.h | 40 +++++----------------------------------- doom-nano.ino | 2 +- 4 files changed, 14 insertions(+), 80 deletions(-) diff --git a/SSD1306.cpp b/SSD1306.cpp index a9387ef..9198dcf 100644 --- a/SSD1306.cpp +++ b/SSD1306.cpp @@ -225,28 +225,7 @@ void Adafruit_SSD1306::drawPixel(int16_t x, int16_t y, uint16_t c case SSD1306_INVERSE: buffer[x + (y/8)*WIDTH] ^= (1 << (y&7)); break; } } -} -/*! - @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: SSD1306_BLACK, SSD1306_WHITE or SSD1306_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. -*/ -template -void Adafruit_SSD1306::drawFastVLine( - int16_t x, int16_t y, int16_t h, uint16_t color) { - drawFastVLineInternal(x, y, h, color); } template @@ -356,26 +335,6 @@ void Adafruit_SSD1306::drawBitmap(int16_t x, int16_t y, } } -/*! - @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 SSD1306_WHITE, unless display invert mode - is enabled), false if clear (SSD1306_BLACK). - @note Reads from buffer contents; may not reflect current contents of - screen if display() has not been called. -*/ -template -bool Adafruit_SSD1306::getPixel(int16_t x, int16_t y) { - if((x >= 0) && (x < WIDTH) && (y >= 0) && (y < HEIGHT)) { - // Pixel is in-bounds. Rotate coordinates if needed. - return (buffer[x + (y / 8) * WIDTH] & (1 << (y & 7))); - } - return false; // Pixel out of bounds -} - // REFRESH DISPLAY --------------------------------------------------------- /*! diff --git a/SSD1306.h b/SSD1306.h index dfcbe26..59a7c39 100644 --- a/SSD1306.h +++ b/SSD1306.h @@ -91,9 +91,6 @@ class Adafruit_SSD1306 { bool begin(uint8_t switchvcc=SSD1306_SWITCHCAPVCC, uint8_t i2caddr=0); void display(void); - void drawPixel(int16_t x, int16_t y, uint16_t color); - void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); - bool getPixel(int16_t x, int16_t y); /*! @brief Clear contents of display buffer (set all pixels to off). @@ -104,6 +101,10 @@ class Adafruit_SSD1306 { */ void clearDisplay(void) { memset(buffer, 0, WIDTH * ((HEIGHT + 7) / 8)); } + // Faster drawPixel than display.drawPixel. + // Avoids some checks to make it faster. + void drawPixel(uint8_t x, uint8_t y, bool color, bool raycasterViewport = false); + /*! @brief Enable or disable display invert mode (white-on-black vs black-on-white). @@ -128,6 +129,10 @@ class Adafruit_SSD1306 { void clearRect(uint8_t, uint8_t, uint8_t, uint8_t); void drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, int16_t h, uint16_t color); + // Faster way to render vertical bits + void drawByte(uint8_t x, uint8_t y, uint8_t b) { + buffer[(y / 8)*SCREEN_WIDTH + x] = b; + } private: void drawFastVLineInternal(int16_t x, int16_t y, int16_t h, uint16_t color); diff --git a/display.h b/display.h index 400bb1f..0a1bf14 100644 --- a/display.h +++ b/display.h @@ -15,9 +15,6 @@ void setupDisplay(); void fps(); bool getGradientPixel(uint8_t x, uint8_t y, uint8_t i); void fadeScreen(uint8_t intensity, bool color); -void drawByte(uint8_t x, uint8_t y, uint8_t b); -uint8_t getByte(uint8_t x, uint8_t y); -void drawPixel(int8_t x, int8_t y, bool color, bool raycasterViewport); void drawVLine(uint8_t x, int8_t start_y, int8_t end_y, uint8_t intensity); void drawSprite(int8_t x, int8_t y, const uint8_t bitmap[], const uint8_t mask[], int16_t w, int16_t h, uint8_t sprite, double distance); void drawChar(int8_t x, int8_t y, char ch); @@ -31,9 +28,6 @@ Adafruit_SSD1306 display; double delta = 1; uint32_t lastFrameTime = 0; -// Optimizations for SSD1306 handles buffer directly -uint8_t *display_buf; - // We don't handle more than MAX_RENDER_DEPTH depth, so we can safety store // z values in a byte with 1 decimal and save some memory, uint8_t zbuffer[ZBUFFER_SIZE]; @@ -46,8 +40,6 @@ void setupDisplay() { while (1); // Don't proceed, loop forever } - display_buf = display.getBuffer(); - // initialize z buffer memset(zbuffer, 0xFF, ZBUFFER_SIZE); } @@ -64,11 +56,6 @@ double getActualFps() { return 1000 / (FRAME_TIME * delta); } -// Faster way to render vertical bits -void drawByte(uint8_t x, uint8_t y, uint8_t b) { - display_buf[(y / 8)*SCREEN_WIDTH + x] = b; -} - boolean getGradientPixel(uint8_t x, uint8_t y, uint8_t i) { if (i == 0) return 0; if (i >= GRADIENT_COUNT - 1) return 1; @@ -85,28 +72,11 @@ void fadeScreen(uint8_t intensity, bool color = 0) { for (uint8_t x = 0; x < SCREEN_WIDTH; x++) { for (uint8_t y = 0; y < SCREEN_HEIGHT; y++) { if (getGradientPixel(x, y, intensity)) - drawPixel(x, y, color, false); + display.drawPixel(x, y, color, false); } } } -// Faster drawPixel than display.drawPixel. -// Avoids some checks to make it faster. -void drawPixel(int8_t x, int8_t y, bool color, bool raycasterViewport = false) { - // prevent write out of screen buffer - if (x < 0 || x >= SCREEN_WIDTH || y < 0 || y >= (raycasterViewport ? RENDER_HEIGHT : SCREEN_HEIGHT)) { - return; - } - - if (color) { - // white - display_buf[x + (y / 8)*SCREEN_WIDTH] |= (1 << (y & 7)); - } else { - // black - display_buf[x + (y / 8)*SCREEN_WIDTH] &= ~(1 << (y & 7)); - } -} - // For raycaster only // Custom draw Vertical lines that fills with a pattern to simulate // different brightness. Affected by RES_DIVIDER @@ -127,7 +97,7 @@ void drawVLine(uint8_t x, int8_t start_y, int8_t end_y, uint8_t intensity) { if (bp == 7) { // write the whole byte - drawByte(x + c, y, b); + display.drawByte(x + c, y, b); b = 0; } @@ -136,7 +106,7 @@ void drawVLine(uint8_t x, int8_t start_y, int8_t end_y, uint8_t intensity) { // draw last byte if (bp != 7) { - drawByte(x + c, y - 1, b); + display.drawByte(x + c, y - 1, b); } } } @@ -187,7 +157,7 @@ void drawSprite( pixel = read_bit(pgm_read_byte(bitmap + byte_offset), sx % 8); for (uint8_t ox = 0; ox < pixel_size; ox++) { for (uint8_t oy = 0; oy < pixel_size; oy++) { - drawPixel(x + tx + ox, y + ty + oy, pixel, true); + display.drawPixel(x + tx + ox, y + ty + oy, pixel, true); } } } @@ -213,7 +183,7 @@ void drawChar(int8_t x, int8_t y, char ch) { b = pgm_read_byte(bmp_font + (line * bmp_font_width + bOffset)); for (n = 0; n < CHAR_WIDTH; n++) if (read_bit(b, (c % 2 == 0 ? 0 : 4) + n)) - drawPixel(x + n, y + line, 1, false); + display.drawPixel(x + n, y + line, 1, false); } } diff --git a/doom-nano.ino b/doom-nano.ino index 52d5e7b..c1fdd0e 100644 --- a/doom-nano.ino +++ b/doom-nano.ino @@ -701,7 +701,7 @@ void loopGamePlay() { fps(); // Clear only the 3d view - memset(display_buf, 0, SCREEN_WIDTH * (RENDER_HEIGHT / 8)); + memset(display.getBuffer(), 0, SCREEN_WIDTH * (RENDER_HEIGHT / 8)); #ifdef SNES_CONTROLLER getControllerData(); From d577c20873b467adb2f0b515b0c200ee10628fc6 Mon Sep 17 00:00:00 2001 From: Dirk Petrautzki Date: Thu, 22 Oct 2020 14:18:24 +0200 Subject: [PATCH 5/6] Add SPI support, minor improvements like changing to uint8_t where possible and removing some sanity checks --- SSD1306.cpp | 195 +++++++++++++++++++++++++--------------------------- SSD1306.h | 18 +++-- display.h | 61 +++++++--------- 3 files changed, 128 insertions(+), 146 deletions(-) diff --git a/SSD1306.cpp b/SSD1306.cpp index 9198dcf..2b4b869 100644 --- a/SSD1306.cpp +++ b/SSD1306.cpp @@ -63,21 +63,39 @@ constexpr uint16_t WIRE_MAX = 342; // 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 ssd1306_command() instead). -template -void Adafruit_SSD1306::ssd1306_command1(uint8_t c) { - uint8_t cmd = 0x00; // Co = 0, D/C = 0 - TWI_Start_Transceiver_With_Data(cmd, &c, 1); +template +void Adafruit_SSD1306::ssd1306_command1(uint8_t c) { + if(DISPLAY_MODE == I2C_MODE) { + uint8_t cmd = 0x00; // Co = 0, D/C = 0 + TWI_Start_Transceiver_With_Data(cmd, &c, 1); + } else { + digitalWrite(dcPin, LOW); + digitalWrite(csPin, LOW); + SPDR = c; + while (!(SPSR & _BV(SPIF))) ; // wait + digitalWrite(csPin, HIGH); + } } // Issue list of commands to SSD1306, same rules as above re: transactions. // This is a private function, not exposed. -template -void Adafruit_SSD1306::ssd1306_commandList(const uint8_t *c, uint8_t n) { - uint8_t cmd = 0x00; // Co = 0, D/C = 0 - uint8_t tmp[n]; - - uint8_t * tmpptr = static_cast(memcpy_P(tmp, c, n)); - TWI_Start_Transceiver_With_Data(cmd, tmpptr, n); +template +void Adafruit_SSD1306::ssd1306_commandList(const uint8_t *c, uint8_t n) { + if(DISPLAY_MODE == I2C_MODE) { + uint8_t cmd = 0x00; // Co = 0, D/C = 0 + uint8_t tmp[n]; + + uint8_t * tmpptr = static_cast(memcpy_P(tmp, c, n)); + TWI_Start_Transceiver_With_Data(cmd, tmpptr, n); + } else { + digitalWrite(dcPin, LOW); + digitalWrite(csPin, LOW); + while(n--) { + SPDR = pgm_read_byte(c++); + while (!(SPSR & _BV(SPIF))) ; // wait + } + digitalWrite(csPin, HIGH); + } } // ALLOCATE & INIT DISPLAY ------------------------------------------------- @@ -117,22 +135,27 @@ void Adafruit_SSD1306::ssd1306_commandList(const uint8_t *c, uint proceeding. @note MUST call this function before any drawing or updates! */ -template -bool Adafruit_SSD1306::begin(uint8_t vcs, uint8_t addr) { +template +bool Adafruit_SSD1306::begin(uint8_t vcs) { clearDisplay(); vccstate = vcs; - // Setup pin directions - // If I2C address is unspecified, use default - // (0x3C for 32-pixel-tall displays, 0x3D for all others). - i2caddr = addr ? addr : ((HEIGHT == 32) ? 0x3C : 0x3D); - // TwoWire begin() function might be already performed by the calling - // function if it has unusual circumstances (e.g. TWI variants that - // can accept different SDA/SCL pins, or if two SSD1306 instances - // with different addresses -- only a single begin() is needed). - TWI_Master_Initialise(); + // TwoWire begin() function might be already performed by the calling + // function if it has unusual circumstances (e.g. TWI variants that + // can accept different SDA/SCL pins, or if two SSD1306 instances + // with different addresses -- only a single begin() is needed). + if(DISPLAY_MODE == I2C_MODE) { + TWI_Master_Initialise(); + } else { // SPI + SPI.beginTransaction(SPISettings(160000000UL, MSBFIRST, SPI_MODE3)); + SPI.begin(); + pinMode(11, OUTPUT); // MOSI and SCLK outputs + pinMode(13 , OUTPUT); + pinMode(dcPin, OUTPUT); // Set data/command pin as output + pinMode(csPin, OUTPUT); // Same for chip select + } // Init sequence static const uint8_t PROGMEM init1[] = { @@ -200,47 +223,28 @@ bool Adafruit_SSD1306::begin(uint8_t vcs, uint8_t addr) { // 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: SSD1306_BLACK, SSD1306_WHITE or SSD1306_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. -*/ -template -void Adafruit_SSD1306::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(color) { - case SSD1306_WHITE: buffer[x + (y/8)*WIDTH] |= (1 << (y&7)); break; - case SSD1306_BLACK: buffer[x + (y/8)*WIDTH] &= ~(1 << (y&7)); break; - case SSD1306_INVERSE: buffer[x + (y/8)*WIDTH] ^= (1 << (y&7)); break; - } +template +void Adafruit_SSD1306::drawPixel(uint8_t x, uint8_t y, bool color, bool raycasterViewport) { + // prevent write out of screen buffer + if (x >= SCREEN_WIDTH || y >= (raycasterViewport ? RENDER_HEIGHT : SCREEN_HEIGHT)) { + return; } + if (color) { + // white + buffer[x + (y / 8)*SCREEN_WIDTH] |= (1 << (y & 7)); + } else { + // black + buffer[x + (y / 8)*SCREEN_WIDTH] &= ~(1 << (y & 7)); + } } -template -void Adafruit_SSD1306::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; - } +template +void Adafruit_SSD1306::drawFastVLineInternal( + uint8_t x, uint8_t __y, uint8_t __h) { if((__y + __h) > HEIGHT) { // Clip bottom __h = (HEIGHT - __y); } - if(__h > 0) { // Proceed only if height is now positive // this display doesn't need ints for coordinates, // use local byte registers for faster juggling uint8_t y = __y, h = __h; @@ -260,11 +264,8 @@ void Adafruit_SSD1306::drawFastVLineInternal( // adjust the mask if we're not going to reach the end of this byte if(h < mod) mask &= (0XFF >> (mod - h)); - switch(color) { - case SSD1306_WHITE: *pBuf |= mask; break; - case SSD1306_BLACK: *pBuf &= ~mask; break; - case SSD1306_INVERSE: *pBuf ^= mask; break; - } + *pBuf &= ~mask; + pBuf += WIDTH; } @@ -272,23 +273,13 @@ void Adafruit_SSD1306::drawFastVLineInternal( h -= mod; // Write solid bytes while we can - effectively 8 rows at a time if(h >= 8) { - if(color == SSD1306_INVERSE) { - // separate copy of the code so we don't impact performance of - // black/white write version with an extra comparison per loop - do { - *pBuf ^= 0xFF; // Invert byte - pBuf += WIDTH; // Advance pointer 8 rows - h -= 8; // Subtract 8 rows from height - } while(h >= 8); - } else { // store a local value to work with - uint8_t val = (color != SSD1306_BLACK) ? 255 : 0; + //uint8_t val = (color != SSD1306_BLACK) ? 255 : 0; do { - *pBuf = val; // Set byte + *pBuf = 0; // Set byte pBuf += WIDTH; // Advance pointer 8 rows h -= 8; // Subtract 8 rows from height } while(h >= 8); - } } if(h) { // Do the final partial byte, if necessary @@ -301,33 +292,27 @@ void Adafruit_SSD1306::drawFastVLineInternal( static const uint8_t PROGMEM postmask[8] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; uint8_t mask = pgm_read_byte(&postmask[mod]); - switch(color) { - case SSD1306_WHITE: *pBuf |= mask; break; - case SSD1306_BLACK: *pBuf &= ~mask; break; - case SSD1306_INVERSE: *pBuf ^= mask; break; - } + *pBuf &= ~mask; } } - } // endif positive height - } // endif x in bounds } -template -void Adafruit_SSD1306::clearRect(uint8_t x, uint8_t y, uint8_t w , uint8_t h) { - for (int16_t i=x; i +void Adafruit_SSD1306::clearRect(uint8_t x, uint8_t y, uint8_t w , uint8_t h) { + for (uint8_t i=x; i -void Adafruit_SSD1306::drawBitmap(int16_t x, int16_t y, - const uint8_t bitmap[], int16_t w, int16_t h, uint16_t color) { +template +void Adafruit_SSD1306::drawBitmap(uint8_t x, uint8_t y, + const uint8_t bitmap[], uint8_t w, uint8_t h, uint8_t color) { - int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte + uint8_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte uint8_t byte = 0; - for(int16_t j=0; j::drawBitmap(int16_t x, int16_t y, called. Call after each graphics command, or after a whole set of graphics commands, as best needed by one's own application. */ -template -void Adafruit_SSD1306::display(void) { +template +void Adafruit_SSD1306::display(void) { static const uint8_t PROGMEM dlist1[] = { SSD1306_PAGEADDR, 0, // Page start address @@ -357,13 +342,23 @@ void Adafruit_SSD1306::display(void) { uint16_t count = WIDTH * ((HEIGHT + 7) / 8); uint8_t *ptr = buffer; - uint8_t cmd = 0x40; - while(count >= (WIRE_MAX)){ - TWI_Start_Transceiver_With_Data(cmd, ptr, WIRE_MAX); - count -= (WIRE_MAX); - ptr += (WIRE_MAX); - } - if(count > 0) { - TWI_Start_Transceiver_With_Data(cmd, ptr, count); + if(DISPLAY_MODE == I2C_MODE) { + uint8_t cmd = 0x40; + while(count >= (WIRE_MAX)){ + TWI_Start_Transceiver_With_Data(cmd, ptr, WIRE_MAX); + count -= (WIRE_MAX); + ptr += (WIRE_MAX); + } + if(count > 0) { + TWI_Start_Transceiver_With_Data(cmd, ptr, count); + } + } else { + digitalWrite(dcPin, HIGH); + digitalWrite(csPin, LOW); + while(count--) { + SPDR = *ptr++; + while (!(SPSR & _BV(SPIF))) ; // wait + } + digitalWrite(csPin, HIGH); } } diff --git a/SSD1306.h b/SSD1306.h index 59a7c39..9d0e2f4 100644 --- a/SSD1306.h +++ b/SSD1306.h @@ -25,6 +25,7 @@ #define _Adafruit_SSD1306_H_ #include "TWI_Master.h" +#include #include "string.h" #include "constants.h" @@ -81,16 +82,15 @@ @brief Class that stores state and functions for interacting with SSD1306 OLED displays. */ -template +template class Adafruit_SSD1306 { public: Adafruit_SSD1306() = default; ~Adafruit_SSD1306(void) = default; - bool begin(uint8_t switchvcc=SSD1306_SWITCHCAPVCC, - uint8_t i2caddr=0); - void display(void); + bool begin(uint8_t switchvcc=SSD1306_SWITCHCAPVCC); + void display(void) __attribute__ ((optimize(3))); /*! @brief Clear contents of display buffer (set all pixels to off). @@ -127,22 +127,20 @@ class Adafruit_SSD1306 { */ uint8_t *getBuffer(void) { return buffer; } void clearRect(uint8_t, uint8_t, uint8_t, uint8_t); - void drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, int16_t h, uint16_t color); - + void drawBitmap(uint8_t x, uint8_t y, const uint8_t bitmap[], uint8_t w, uint8_t h, uint8_t color) __attribute__ ((optimize(3))); // Faster way to render vertical bits void drawByte(uint8_t x, uint8_t y, uint8_t b) { buffer[(y / 8)*SCREEN_WIDTH + x] = b; } private: - void drawFastVLineInternal(int16_t x, int16_t y, int16_t h, - uint16_t color); + void drawFastVLineInternal(uint8_t x, uint8_t y, uint8_t h) __attribute__ ((optimize(3))); void ssd1306_command1(uint8_t c); void ssd1306_commandList(const uint8_t *c, uint8_t n); uint8_t buffer[WIDTH * ((HEIGHT + 7) / 8)]; - int8_t i2caddr, vccstate, page_end; + int8_t vccstate; }; -template class Adafruit_SSD1306; +template class Adafruit_SSD1306; #endif // _Adafruit_SSD1306_H_ diff --git a/display.h b/display.h index 0a1bf14..062f7ce 100644 --- a/display.h +++ b/display.h @@ -13,16 +13,16 @@ const static uint8_t PROGMEM bit_mask[8] = { 128, 64, 32, 16, 8, 4, 2, 1 }; void setupDisplay(); void fps(); -bool getGradientPixel(uint8_t x, uint8_t y, uint8_t i); +bool getGradientPixel(uint8_t x, uint8_t y, uint8_t i) __attribute__ ((optimize(3))); void fadeScreen(uint8_t intensity, bool color); -void drawVLine(uint8_t x, int8_t start_y, int8_t end_y, uint8_t intensity); -void drawSprite(int8_t x, int8_t y, const uint8_t bitmap[], const uint8_t mask[], int16_t w, int16_t h, uint8_t sprite, double distance); -void drawChar(int8_t x, int8_t y, char ch); -void drawText(int8_t x, int8_t y, char *txt, uint8_t space = 1); -void drawText(int8_t x, int8_t y, const __FlashStringHelper txt, uint8_t space = 1); +void drawVLine(uint8_t x, uint8_t start_y, uint8_t end_y, uint8_t intensity) __attribute__ ((optimize(3))); +void drawSprite(uint8_t x, uint8_t y, const uint8_t bitmap[], const uint8_t mask[], int16_t w, int16_t h, uint8_t sprite, double distance) __attribute__ ((optimize(3))); +void drawChar(uint8_t x, uint8_t y, char ch) __attribute__ ((optimize(3))); +void drawText(uint8_t x, uint8_t y, char *txt, uint8_t space = 1) __attribute__ ((optimize(3))); +void drawText(uint8_t x, uint8_t y, const __FlashStringHelper txt, uint8_t space = 1) __attribute__ ((optimize(3))); // Initialize screen. Following line is for OLED 128x64 connected by I2C -Adafruit_SSD1306 display; +Adafruit_SSD1306 display; // FPS control double delta = 1; @@ -35,7 +35,7 @@ uint8_t zbuffer[ZBUFFER_SIZE]; void setupDisplay() { // Setup display // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally - if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Fixed from 0x3D + if (!display.begin(SSD1306_SWITCHCAPVCC)) { Serial.println(F("SSD1306 allocation failed")); while (1); // Don't proceed, loop forever } @@ -80,20 +80,16 @@ void fadeScreen(uint8_t intensity, bool color = 0) { // For raycaster only // Custom draw Vertical lines that fills with a pattern to simulate // different brightness. Affected by RES_DIVIDER -void drawVLine(uint8_t x, int8_t start_y, int8_t end_y, uint8_t intensity) { - int8_t y; - int8_t lower_y = max(min(start_y, end_y), 0); - int8_t higher_y = min(max(start_y, end_y), RENDER_HEIGHT - 1); - uint8_t c; +void drawVLine(uint8_t x, uint8_t start_y, uint8_t end_y, uint8_t intensity) { + uint8_t higher_y = min(end_y, RENDER_HEIGHT - 1); - uint8_t bp; - uint8_t b; - for (c = 0; c < RES_DIVIDER; c++) { - y = lower_y; - b = 0; + for (uint8_t c = 0; c < RES_DIVIDER; ++c) { + uint8_t y = start_y; + uint8_t b = 0; + uint8_t bp; while (y <= higher_y) { - bp = y % 8; - b = b | getGradientPixel(x + c, y, intensity) << bp; + bp = y & 0x07; + b |= getGradientPixel(x + c, y, intensity) << bp; if (bp == 7) { // write the whole byte @@ -101,7 +97,7 @@ void drawVLine(uint8_t x, int8_t start_y, int8_t end_y, uint8_t intensity) { b = 0; } - y++; + ++y; } // draw last byte @@ -113,7 +109,7 @@ void drawVLine(uint8_t x, int8_t start_y, int8_t end_y, uint8_t intensity) { // Custom drawBitmap method with scale support, mask, zindex and pattern filling void drawSprite( - int8_t x, int8_t y, + uint8_t x, uint8_t y, const uint8_t bitmap[], const uint8_t mask[], int16_t w, int16_t h, uint8_t sprite, @@ -168,7 +164,7 @@ void drawSprite( // Draw a single character. // Made for a custom font with some useful sprites. Char size 4 x 6 // Uses less memory than display.print() -void drawChar(int8_t x, int8_t y, char ch) { +void drawChar(uint8_t x, uint8_t y, char ch) { uint8_t c = 0; uint8_t n; uint8_t bOffset; @@ -188,22 +184,15 @@ void drawChar(int8_t x, int8_t y, char ch) { } // Draw a string -void drawText(int8_t x, int8_t y, char *txt, uint8_t space) { - uint8_t pos = x; - uint8_t i = 0; - char ch; - while ((ch = txt[i]) != '\0') { - drawChar(pos, y, ch); - i++; - pos += CHAR_WIDTH + space; - - // shortcut on end of screen - if (pos > SCREEN_WIDTH) return; - } +void drawText(uint8_t x, uint8_t y, char *txt, uint8_t space) { + do { + drawChar(x, y, *txt++); + x += CHAR_WIDTH + space; + } while(*txt); } // Draw a F() string -void drawText(int8_t x, int8_t y, const __FlashStringHelper *txt_p, uint8_t space = 1) { +void drawText(uint8_t x, uint8_t y, const __FlashStringHelper *txt_p, uint8_t space = 1) { uint8_t pos = x; uint8_t i = 0; char ch; From e7a1c27883cdd1beff36422954b9e1c374ba60ff Mon Sep 17 00:00:00 2001 From: Dirk Petrautzki Date: Thu, 22 Oct 2020 14:20:27 +0200 Subject: [PATCH 6/6] change frame time to enable higher fps --- constants.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constants.h b/constants.h index a0cb0db..97446b6 100644 --- a/constants.h +++ b/constants.h @@ -21,7 +21,7 @@ constexpr uint8_t SOUND_PIN = 9; // do not change, belongs to used timer // GFX settings -#define FRAME_TIME 66.666666 // Desired time per frame in ms (66.666666 is ~15 fps) +#define FRAME_TIME 40 // Desired time per frame in ms (40 is 25 fps) #define RES_DIVIDER 2 // Higher values will result in lower horizontal resolution when rasterize and lower process and memory usage // Lower will require more process and memory, but looks nicer #define Z_RES_DIVIDER 2 // Zbuffer resolution divider. We sacrifice resolution to save memory