From 745b84cb00a3b14e3ffb13a3087a4cd0bde21e1d Mon Sep 17 00:00:00 2001 From: Drashna Jael're Date: Wed, 11 Jan 2023 12:55:52 -0800 Subject: [PATCH 01/26] Improve oled driver code --- builddefs/common_features.mk | 18 +- drivers/oled/oled_common.h | 94 +++ .../sh110x.c => drivers/oled/oled_driver.c | 239 +++--- drivers/oled/oled_driver.h | 42 + drivers/oled/oled_driver_i2c.c | 58 ++ drivers/oled/oled_driver_spi.c | 85 ++ drivers/oled/ssd1306_sh1106.c | 795 ------------------ keyboards/adafruit/macropad/rules.mk | 5 +- .../dilemma/3x5_2/keymaps/drashna/rules.mk | 1 - .../5x6_right/keymaps/drashna/config.h | 5 +- .../5x6_right/keymaps/drashna/rules.mk | 1 - .../splitkb/kyria/keymaps/drashna/config.h | 7 +- .../splitkb/kyria/keymaps/drashna/keymap.c | 4 - .../splitkb/kyria/keymaps/drashna/rules.mk | 1 - users/drashna/oled/oled.mk | 9 + users/drashna/oled/oled_config.h | 21 - 16 files changed, 403 insertions(+), 982 deletions(-) create mode 100644 drivers/oled/oled_common.h rename users/drashna/oled/sh110x.c => drivers/oled/oled_driver.c (81%) create mode 100644 drivers/oled/oled_driver_i2c.c create mode 100644 drivers/oled/oled_driver_spi.c delete mode 100644 drivers/oled/ssd1306_sh1106.c create mode 100644 users/drashna/oled/oled.mk diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk index dc64ed2394e0..c04d57214240 100644 --- a/builddefs/common_features.mk +++ b/builddefs/common_features.mk @@ -735,20 +735,30 @@ ifeq ($(strip $(HD44780_ENABLE)), yes) SRC += hd44780.c endif -VALID_OLED_DRIVER_TYPES := SSD1306 custom -OLED_DRIVER ?= SSD1306 +VALID_OLED_DRIVER_TYPES := I2C SPI SSD1306 custom +OLED_DRIVER ?= I2C ifeq ($(strip $(OLED_ENABLE)), yes) ifeq ($(filter $(OLED_DRIVER),$(VALID_OLED_DRIVER_TYPES)),) $(call CATASTROPHIC_ERROR,Invalid OLED_DRIVER,OLED_DRIVER="$(OLED_DRIVER)" is not a valid OLED driver) else OPT_DEFS += -DOLED_ENABLE COMMON_VPATH += $(DRIVER_PATH)/oled + ifneq ($(strip $(OLED_DRIVER)), custom) + SRC += oled_driver.c + endif + ifeq ($(strip $(OLED_DRIVER)), SSD1306) + OLED_DRIVER = I2C + endif OPT_DEFS += -DOLED_DRIVER_$(strip $(shell echo $(OLED_DRIVER) | tr '[:lower:]' '[:upper:]')) - ifeq ($(strip $(OLED_DRIVER)), SSD1306) - SRC += ssd1306_sh1106.c + ifeq ($(strip $(OLED_DRIVER)), I2C) + SRC += oled_driver_i2c.c QUANTUM_LIB_SRC += i2c_master.c endif + ifeq ($(strip $(OLED_DRIVER)), SPI) + SRC += oled_driver_spi.c + QUANTUM_LIB_SRC += spi_master.c + endif endif endif diff --git a/drivers/oled/oled_common.h b/drivers/oled/oled_common.h new file mode 100644 index 000000000000..c1233173fd5a --- /dev/null +++ b/drivers/oled/oled_common.h @@ -0,0 +1,94 @@ +#pragma once + +#include "progmem.h" + +// Fundamental Commands +#define CONTRAST 0x81 +#define DISPLAY_ALL_ON 0xA5 +#define DISPLAY_ALL_ON_RESUME 0xA4 +#define NORMAL_DISPLAY 0xA6 +#define INVERT_DISPLAY 0xA7 +#define DISPLAY_ON 0xAF +#define DISPLAY_OFF 0xAE +#define NOP 0xE3 + +// Scrolling Commands +#define ACTIVATE_SCROLL 0x2F +#define DEACTIVATE_SCROLL 0x2E +#define SCROLL_RIGHT 0x26 +#define SCROLL_LEFT 0x27 +#define SCROLL_RIGHT_UP 0x29 +#define SCROLL_LEFT_UP 0x2A + +// Addressing Setting Commands +#define MEMORY_MODE 0x20 +#define COLUMN_ADDR 0x21 +#define PAGE_ADDR 0x22 +#define PAM_SETCOLUMN_LSB 0x00 +#define PAM_SETCOLUMN_MSB 0x10 +#define PAM_PAGE_ADDR 0xB0 // 0xb0 -- 0xb7 + +// Hardware Configuration Commands +#define DISPLAY_START_LINE 0x40 +#define SEGMENT_REMAP 0xA0 +#define SEGMENT_REMAP_INV 0xA1 +#define MULTIPLEX_RATIO 0xA8 +#define COM_SCAN_INC 0xC0 +#define COM_SCAN_DEC 0xC8 +#define DISPLAY_OFFSET 0xD3 +#define COM_PINS 0xDA +#define COM_PINS_SEQ 0x02 +#define COM_PINS_ALT 0x12 +#define COM_PINS_SEQ_LR 0x22 +#define COM_PINS_ALT_LR 0x32 + +// Timing & Driving Commands +#define DISPLAY_CLOCK 0xD5 +#define PRE_CHARGE_PERIOD 0xD9 +#define VCOM_DETECT 0xDB + +// Advance Graphic Commands +#define FADE_BLINK 0x23 +#define ENABLE_FADE 0x20 +#define ENABLE_BLINK 0x30 + +// Charge Pump Commands +#define CHARGE_PUMP 0x8D + +// Commands specific to the SH1107 chip +#define SH1107_DISPLAY_START_LINE 0xDC +#define SH1107_MEMORY_MODE_PAGE 0x20 +#define SH1107_MEMORY_MODE_VERTICAL 0x21 + +// Misc defines +#ifndef OLED_BLOCK_COUNT +# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) +#endif +#ifndef OLED_BLOCK_SIZE +# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) +#endif + +#define OLED_ALL_BLOCKS_MASK (((((OLED_BLOCK_TYPE)1 << (OLED_BLOCK_COUNT - 1)) - 1) << 1) | 1) + +#define OLED_IC_HAS_HORIZONTAL_MODE (OLED_IC == OLED_IC_SSD1306) +#define OLED_IC_COM_PINS_ARE_COLUMNS (OLED_IC == OLED_IC_SH1107) + +#ifndef OLED_COM_PIN_COUNT +# if OLED_IC == OLED_IC_SSD1306 +# define OLED_COM_PIN_COUNT 64 +# elif OLED_IC == OLED_IC_SH1106 +# define OLED_COM_PIN_COUNT 64 +# elif OLED_IC == OLED_IC_SH1107 +# define OLED_COM_PIN_COUNT 128 +# else +# error Invalid OLED_IC value +# endif +#endif + +#ifndef OLED_COM_PIN_OFFSET +# define OLED_COM_PIN_OFFSET 0 +#endif + +// i2c defines +#define I2C_CMD 0x00 +#define I2C_DATA 0x40 diff --git a/users/drashna/oled/sh110x.c b/drivers/oled/oled_driver.c similarity index 81% rename from users/drashna/oled/sh110x.c rename to drivers/oled/oled_driver.c index f96a93a8973a..e607d8927618 100644 --- a/users/drashna/oled/sh110x.c +++ b/drivers/oled/oled_driver.c @@ -14,106 +14,22 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#include "i2c_master.h" + #include "oled_driver.h" +#include "oled_common.h" #include OLED_FONT_H #include "timer.h" #include "print.h" - #include - #include "progmem.h" - #include "keyboard.h" +// Used commands from spec sheet: https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf +// for SH1106: https://www.velleman.eu/downloads/29/infosheets/sh1106_datasheet.pdf // for SH1107: https://www.displayfuture.com/Display/datasheet/controller/SH1107.pdf -// Fundamental Commands -#define CONTRAST 0x81 -#define DISPLAY_ALL_ON 0xA5 -#define DISPLAY_ALL_ON_RESUME 0xA4 -#define NORMAL_DISPLAY 0xA6 -#define INVERT_DISPLAY 0xA7 -#define DISPLAY_ON 0xAF -#define DISPLAY_OFF 0xAE -#define NOP 0xE3 - -// Scrolling Commands -#define ACTIVATE_SCROLL 0x2F -#define DEACTIVATE_SCROLL 0x2E -#define SCROLL_RIGHT 0x26 -#define SCROLL_LEFT 0x27 -#define SCROLL_RIGHT_UP 0x29 -#define SCROLL_LEFT_UP 0x2A - -// Addressing Setting Commands -#define MEMORY_MODE 0x20 -#define COLUMN_ADDR 0x21 -#define PAGE_ADDR 0x22 -#define PAM_SETCOLUMN_LSB 0x00 -#define PAM_SETCOLUMN_MSB 0x10 -#define PAM_PAGE_ADDR 0xB0 // 0xb0 -- 0xb7 - -// Hardware Configuration Commands -#define DISPLAY_START_LINE 0x40 -#define SEGMENT_REMAP 0xA0 -#define SEGMENT_REMAP_INV 0xA1 -#define MULTIPLEX_RATIO 0xA8 -#define COM_SCAN_INC 0xC0 -#define COM_SCAN_DEC 0xC8 -#define DISPLAY_OFFSET 0xD3 -#define COM_PINS 0xDA -#define COM_PINS_SEQ 0x02 -#define COM_PINS_ALT 0x12 -#define COM_PINS_SEQ_LR 0x22 -#define COM_PINS_ALT_LR 0x32 - -// Timing & Driving Commands -#define DISPLAY_CLOCK 0xD5 -#define PRE_CHARGE_PERIOD 0xD9 -#define VCOM_DETECT 0xDB - -// Advance Graphic Commands -#define FADE_BLINK 0x23 -#define ENABLE_FADE 0x20 -#define ENABLE_BLINK 0x30 - -// Charge Pump Commands -#define CHARGE_PUMP 0x8D - -// Commands specific to the SH1107 chip -#define SH1107_DISPLAY_START_LINE 0xDC -#define SH1107_MEMORY_MODE_PAGE 0x20 -#define SH1107_MEMORY_MODE_VERTICAL 0x21 - -// Misc defines -#ifndef OLED_BLOCK_COUNT -# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) -#endif -#ifndef OLED_BLOCK_SIZE -# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) -#endif - -#define OLED_ALL_BLOCKS_MASK (((((OLED_BLOCK_TYPE)1 << (OLED_BLOCK_COUNT - 1)) - 1) << 1) | 1) - -#ifndef OLED_COM_PIN_COUNT -# define OLED_COM_PIN_COUNT 128 -#endif - -#ifndef OLED_COM_PIN_OFFSET -# define OLED_COM_PIN_OFFSET 0 -#endif - -// i2c defines -#define I2C_CMD 0x00 -#define I2C_DATA 0x40 -#if defined(__AVR__) -# define I2C_TRANSMIT_P(data) i2c_transmit_P((OLED_DISPLAY_ADDRESS << 1), &data[0], sizeof(data), OLED_I2C_TIMEOUT) -#else // defined(__AVR__) -# define I2C_TRANSMIT_P(data) i2c_transmit((OLED_DISPLAY_ADDRESS << 1), &data[0], sizeof(data), OLED_I2C_TIMEOUT) -#endif // defined(__AVR__) -#define I2C_TRANSMIT(data) i2c_transmit((OLED_DISPLAY_ADDRESS << 1), &data[0], sizeof(data), OLED_I2C_TIMEOUT) -#define I2C_WRITE_REG(mode, data, size) i2c_writeReg((OLED_DISPLAY_ADDRESS << 1), mode, data, size, OLED_I2C_TIMEOUT) +#undef ARRAY_SIZE +#define ARRAY_SIZE(arr) sizeof(arr)/sizeof(arr[0]) #define HAS_FLAGS(bits, flags) ((bits & flags) == flags) @@ -122,7 +38,7 @@ along with this program. If not, see . // parts of the display unusable or don't get cleared correctly // and also allows for drawing & inverting uint8_t oled_buffer[OLED_MATRIX_SIZE]; -uint8_t *oled_cursor; +uint8_t * oled_cursor; OLED_BLOCK_TYPE oled_dirty = 0; bool oled_initialized = false; bool oled_active = false; @@ -144,24 +60,22 @@ uint32_t oled_scroll_timeout; uint16_t oled_update_timeout; #endif -// Internal variables to reduce math instructions - -#if defined(__AVR__) -// identical to i2c_transmit, but for PROGMEM since all initialization is in PROGMEM arrays currently -// probably should move this into i2c_master... -static i2c_status_t i2c_transmit_P(uint8_t address, const uint8_t *data, uint16_t length, uint16_t timeout) { - i2c_status_t status = i2c_start(address | I2C_WRITE, timeout); - for (uint16_t i = 0; i < length && status >= 0; i++) { - status = i2c_write(pgm_read_byte((const char *)data++), timeout); - if (status) break; - } +// Transmit/Write Funcs. +__attribute__((weak)) bool oled_cmd(const uint8_t *data, uint16_t size) { + return false; +} - i2c_stop(); +__attribute__((weak)) bool oled_cmd_P(const uint8_t *data, uint16_t size) { + return oled_cmd(data, size); +} - return status; +__attribute__((weak)) bool oled_write_reg(const uint8_t *data, uint16_t size) { + return false; } -#endif + +__attribute__((weak)) void oled_driver_init(void) {} + // Flips the rendering bits for a character at the current cursor position static void InvertCharacter(uint8_t *cursor) { @@ -185,7 +99,8 @@ bool oled_init(oled_rotation_t rotation) { } else { oled_rotation_width = OLED_DISPLAY_HEIGHT; } - i2c_init(); + oled_driver_init(); + static const uint8_t PROGMEM display_setup1[] = { I2C_CMD, @@ -193,14 +108,29 @@ bool oled_init(oled_rotation_t rotation) { DISPLAY_CLOCK, 0x80, MULTIPLEX_RATIO, +#if OLED_IC_COM_PINS_ARE_COLUMNS OLED_DISPLAY_WIDTH - 1, - SH1107_DISPLAY_START_LINE, - 0x00, +#else + OLED_DISPLAY_HEIGHT - 1, +#endif +#if OLED_IC == OLED_IC_SH1107 + SH1107_DISPLAY_START_LINE, + 0x00, +#else + DISPLAY_START_LINE | 0x00, +#endif CHARGE_PUMP, 0x14, - SH1107_MEMORY_MODE_PAGE, +#if OLED_IC_HAS_HORIZONTAL_MODE + // MEMORY_MODE is unsupported on SH1106 (Page Addressing only) + MEMORY_MODE, + 0x00, // Horizontal addressing mode +#elif OLED_IC == OLED_IC_SH1107 + // Page addressing mode + SH1107_MEMORY_MODE_PAGE, +#endif }; - if (I2C_TRANSMIT_P(display_setup1) != I2C_STATUS_SUCCESS) { + if (!oled_cmd_P(display_setup1, ARRAY_SIZE(display_setup1))) { print("oled_init cmd set 1 failed\n"); return false; } @@ -213,7 +143,7 @@ bool oled_init(oled_rotation_t rotation) { DISPLAY_OFFSET, OLED_COM_PIN_OFFSET, }; - if (I2C_TRANSMIT_P(display_normal) != I2C_STATUS_SUCCESS) { + if (!oled_cmd_P(display_normal, ARRAY_SIZE(display_normal))) { print("oled_init cmd normal rotation failed\n"); return false; } @@ -225,24 +155,14 @@ bool oled_init(oled_rotation_t rotation) { DISPLAY_OFFSET, (OLED_COM_PIN_COUNT - OLED_COM_PIN_OFFSET) % OLED_COM_PIN_COUNT, }; - if (I2C_TRANSMIT_P(display_flipped) != I2C_STATUS_SUCCESS) { + if (!oled_cmd_P(display_flipped, ARRAY_SIZE(display_flipped))) { print("display_flipped failed\n"); return false; } } - static const uint8_t PROGMEM display_setup2[] = { - I2C_CMD, COM_PINS, - OLED_COM_PINS, - CONTRAST, OLED_BRIGHTNESS, - PRE_CHARGE_PERIOD, 0x22, - VCOM_DETECT, 0x35, - DISPLAY_ALL_ON_RESUME, - NORMAL_DISPLAY, - DEACTIVATE_SCROLL, - DISPLAY_ON - }; - if (I2C_TRANSMIT_P(display_setup2) != I2C_STATUS_SUCCESS) { + static const uint8_t PROGMEM display_setup2[] = {I2C_CMD, COM_PINS, OLED_COM_PINS, CONTRAST, OLED_BRIGHTNESS, PRE_CHARGE_PERIOD, 0xF1, VCOM_DETECT, 0x20, DISPLAY_ALL_ON_RESUME, NORMAL_DISPLAY, DEACTIVATE_SCROLL, DISPLAY_ON}; + if (!oled_cmd_P(display_setup2, ARRAY_SIZE(display_setup2))) { print("display_setup2 failed\n"); return false; } @@ -278,11 +198,19 @@ static void calc_bounds(uint8_t update_start, uint8_t *cmd_array) { // Calculate commands to set memory addressing bounds. uint8_t start_page = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_WIDTH; uint8_t start_column = OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_WIDTH; +#if !OLED_IC_HAS_HORIZONTAL_MODE // Commands for Page Addressing Mode. Sets starting page and column; has no end bound. // Column value must be split into high and low nybble and sent as two commands. cmd_array[0] = PAM_PAGE_ADDR | start_page; cmd_array[1] = PAM_SETCOLUMN_LSB | ((OLED_COLUMN_OFFSET + start_column) & 0x0f); cmd_array[2] = PAM_SETCOLUMN_MSB | ((OLED_COLUMN_OFFSET + start_column) >> 4 & 0x0f); +#else + // Commands for use in Horizontal Addressing mode. + cmd_array[1] = start_column + OLED_COLUMN_OFFSET; + cmd_array[4] = start_page; + cmd_array[2] = (OLED_BLOCK_SIZE + OLED_DISPLAY_WIDTH - 1) % OLED_DISPLAY_WIDTH + cmd_array[1]; + cmd_array[5] = (OLED_BLOCK_SIZE + OLED_DISPLAY_WIDTH - 1) / OLED_DISPLAY_WIDTH - 1 + cmd_array[4]; +#endif } static void calc_bounds_90(uint8_t update_start, uint8_t *cmd_array) { @@ -300,12 +228,19 @@ static void calc_bounds_90(uint8_t update_start, uint8_t *cmd_array) { // Top page number for a block which is at the bottom edge of the screen. const uint8_t bottom_block_top_page = (height_in_pages - page_inc_per_block) % height_in_pages; +#if !OLED_IC_HAS_HORIZONTAL_MODE // Only the Page Addressing Mode is supported uint8_t start_page = bottom_block_top_page - (OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_HEIGHT / 8); uint8_t start_column = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_HEIGHT * 8; - cmd_array[0] = PAM_PAGE_ADDR | start_page; - cmd_array[1] = PAM_SETCOLUMN_LSB | ((OLED_COLUMN_OFFSET + start_column) & 0x0f); - cmd_array[2] = PAM_SETCOLUMN_MSB | ((OLED_COLUMN_OFFSET + start_column) >> 4 & 0x0f); + cmd_array[0] = PAM_PAGE_ADDR | start_page; + cmd_array[1] = PAM_SETCOLUMN_LSB | ((OLED_COLUMN_OFFSET + start_column) & 0x0f); + cmd_array[2] = PAM_SETCOLUMN_MSB | ((OLED_COLUMN_OFFSET + start_column) >> 4 & 0x0f); +#else + cmd_array[1] = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_HEIGHT * 8 + OLED_COLUMN_OFFSET; + cmd_array[4] = bottom_block_top_page - (OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_HEIGHT / 8); + cmd_array[2] = (OLED_BLOCK_SIZE + OLED_DISPLAY_HEIGHT - 1) / OLED_DISPLAY_HEIGHT * 8 - 1 + cmd_array[1]; + cmd_array[5] = (OLED_BLOCK_SIZE + OLED_DISPLAY_HEIGHT - 1) % OLED_DISPLAY_HEIGHT / 8 + cmd_array[4]; +#endif } uint8_t crot(uint8_t a, int8_t n) { @@ -333,7 +268,7 @@ void oled_render(void) { // Turn on display if it is off oled_on(); - uint8_t update_start = 0; + uint8_t update_start = 0; uint8_t num_processed = 0; while (oled_dirty && num_processed++ < OLED_UPDATE_PROCESS_LIMIT) { // render all dirty blocks (up to the configured limit) // Find next dirty block @@ -342,7 +277,11 @@ void oled_render(void) { } // Set column & page position +#if OLED_IC_HAS_HORIZONTAL_MODE + static uint8_t display_start[] = {I2C_CMD, COLUMN_ADDR, 0, OLED_DISPLAY_WIDTH - 1, PAGE_ADDR, 0, OLED_DISPLAY_HEIGHT / 8 - 1}; +#else static uint8_t display_start[] = {I2C_CMD, PAM_PAGE_ADDR, PAM_SETCOLUMN_LSB, PAM_SETCOLUMN_MSB}; +#endif if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { calc_bounds(update_start, &display_start[1]); // Offset from I2C_CMD byte at the start } else { @@ -350,14 +289,14 @@ void oled_render(void) { } // Send column & page position - if (I2C_TRANSMIT(display_start) != I2C_STATUS_SUCCESS) { + if (!oled_cmd(display_start, ARRAY_SIZE(display_start))) { print("oled_render offset command failed\n"); - return; - } + return; + } if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { // Send render data chunk as is - if (I2C_WRITE_REG(I2C_DATA, &oled_buffer[OLED_BLOCK_SIZE * update_start], OLED_BLOCK_SIZE) != I2C_STATUS_SUCCESS) { + if (!oled_write_reg(&oled_buffer[OLED_BLOCK_SIZE * update_start], OLED_BLOCK_SIZE)) { print("oled_render data failed\n"); return; } @@ -372,27 +311,35 @@ void oled_render(void) { rotate_90(&oled_buffer[OLED_BLOCK_SIZE * update_start + source_map[i]], &temp_buffer[target_map[i]]); } +#if OLED_IC_HAS_HORIZONTAL_MODE + // Send render data chunk after rotating + if (!oled_write_reg(&temp_buffer[0], OLED_BLOCK_SIZE)) { + print("oled_render90 data failed\n"); + return; + } +#else // For SH1106 or SH1107 the data chunk must be split into separate pieces for each page const uint8_t columns_in_block = (OLED_BLOCK_SIZE + OLED_DISPLAY_HEIGHT - 1) / OLED_DISPLAY_HEIGHT * 8; - const uint8_t num_pages = OLED_BLOCK_SIZE / columns_in_block; + const uint8_t num_pages = OLED_BLOCK_SIZE / columns_in_block; for (uint8_t i = 0; i < num_pages; ++i) { // Send column & page position for all pages except the first one if (i > 0) { display_start[1]++; - if (I2C_TRANSMIT(display_start) != I2C_STATUS_SUCCESS) { + if (!oled_cmd(display_start, ARRAY_SIZE(display_start))) { print("oled_render offset command failed\n"); return; } } // Send data for the page - if (I2C_WRITE_REG(I2C_DATA, &temp_buffer[columns_in_block * i], columns_in_block) != I2C_STATUS_SUCCESS) { + if (!oled_write_reg(&temp_buffer[columns_in_block * i], columns_in_block)) { print("oled_render90 data failed\n"); return; } } +#endif } - // Clear dirty flag + // Clear dirty flag of just rendered block oled_dirty &= ~((OLED_BLOCK_TYPE)1 << update_start); } } @@ -544,6 +491,8 @@ void oled_pan_section(bool left, uint16_t y_start, uint16_t y_end, uint16_t x_st } } + + oled_buffer_reader_t oled_read_raw(uint16_t start_index) { if (start_index > OLED_MATRIX_SIZE) start_index = OLED_MATRIX_SIZE; oled_buffer_reader_t ret_reader; @@ -633,7 +582,7 @@ bool oled_on(void) { #endif if (!oled_active) { - if (I2C_TRANSMIT_P(display_on) != I2C_STATUS_SUCCESS) { + if (!oled_cmd_P(display_on, ARRAY_SIZE(display_on))) { print("oled_on cmd failed\n"); return oled_active; } @@ -655,7 +604,7 @@ bool oled_off(void) { #endif if (oled_active) { - if (I2C_TRANSMIT_P(display_off) != I2C_STATUS_SUCCESS) { + if (!oled_cmd_P(display_off, ARRAY_SIZE(display_off))) { print("oled_off cmd failed\n"); return oled_active; } @@ -675,7 +624,7 @@ uint8_t oled_set_brightness(uint8_t level) { uint8_t set_contrast[] = {I2C_CMD, CONTRAST, level}; if (oled_brightness != level) { - if (I2C_TRANSMIT(set_contrast) != I2C_STATUS_SUCCESS) { + if (!oled_cmd(set_contrast, ARRAY_SIZE(set_contrast))) { print("set_brightness cmd failed\n"); return oled_brightness; } @@ -722,7 +671,7 @@ bool oled_scroll_right(void) { // This prevents scrolling of bad data from starting the scroll too early after init if (!oled_dirty && !oled_scrolling) { uint8_t display_scroll_right[] = {I2C_CMD, SCROLL_RIGHT, 0x00, oled_scroll_start, oled_scroll_speed, oled_scroll_end, 0x00, 0xFF, ACTIVATE_SCROLL}; - if (I2C_TRANSMIT(display_scroll_right) != I2C_STATUS_SUCCESS) { + if (!oled_cmd(display_scroll_right, ARRAY_SIZE(display_scroll_right))) { print("oled_scroll_right cmd failed\n"); return oled_scrolling; } @@ -740,7 +689,7 @@ bool oled_scroll_left(void) { // This prevents scrolling of bad data from starting the scroll too early after init if (!oled_dirty && !oled_scrolling) { uint8_t display_scroll_left[] = {I2C_CMD, SCROLL_LEFT, 0x00, oled_scroll_start, oled_scroll_speed, oled_scroll_end, 0x00, 0xFF, ACTIVATE_SCROLL}; - if (I2C_TRANSMIT(display_scroll_left) != I2C_STATUS_SUCCESS) { + if (!oled_cmd(display_scroll_left, ARRAY_SIZE(display_scroll_left))) { print("oled_scroll_left cmd failed\n"); return oled_scrolling; } @@ -756,7 +705,7 @@ bool oled_scroll_off(void) { if (oled_scrolling) { static const uint8_t PROGMEM display_scroll_off[] = {I2C_CMD, DEACTIVATE_SCROLL}; - if (I2C_TRANSMIT_P(display_scroll_off) != I2C_STATUS_SUCCESS) { + if (!oled_cmd_P(display_scroll_off, ARRAY_SIZE(display_scroll_off))) { print("oled_scroll_off cmd failed\n"); return oled_scrolling; } @@ -777,14 +726,14 @@ bool oled_invert(bool invert) { if (invert && !oled_inverted) { static const uint8_t PROGMEM display_inverted[] = {I2C_CMD, INVERT_DISPLAY}; - if (I2C_TRANSMIT_P(display_inverted) != I2C_STATUS_SUCCESS) { + if (!oled_cmd_P(display_inverted, ARRAY_SIZE(display_inverted))) { print("oled_invert cmd failed\n"); return oled_inverted; } oled_inverted = true; } else if (!invert && oled_inverted) { static const uint8_t PROGMEM display_normal[] = {I2C_CMD, NORMAL_DISPLAY}; - if (I2C_TRANSMIT_P(display_normal) != I2C_STATUS_SUCCESS) { + if (!oled_cmd_P(display_normal, ARRAY_SIZE(display_normal))) { print("oled_invert cmd failed\n"); return oled_inverted; } @@ -821,7 +770,7 @@ void oled_task(void) { } #else oled_set_cursor(0, 0); - oled_task_kbr(); + oled_task_kb(); #endif #if OLED_SCROLL_TIMEOUT > 0 diff --git a/drivers/oled/oled_driver.h b/drivers/oled/oled_driver.h index 291049e36bd7..4d7f2c1d3326 100644 --- a/drivers/oled/oled_driver.h +++ b/drivers/oled/oled_driver.h @@ -22,6 +22,7 @@ along with this program. If not, see . // an enumeration of the chips this driver supports #define OLED_IC_SSD1306 0 #define OLED_IC_SH1106 1 +#define OLED_IC_SH1107 2 #if defined(OLED_DISPLAY_CUSTOM) // Expected user to implement the necessary defines @@ -68,6 +69,40 @@ along with this program. If not, see . // If OLED_BLOCK_TYPE is uint8_t, these tables would look like: // #define OLED_SOURCE_MAP { 0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120 } // #define OLED_TARGET_MAP { 56, 120, 48, 112, 40, 104, 32, 96, 24, 88, 16, 80, 8, 72, 0, 64 } +#elif defined(OLED_DISPLAY_128X128) +// Quad height 128x128 +# ifndef OLED_DISPLAY_WIDTH +# define OLED_DISPLAY_WIDTH 128 +# endif +# ifndef OLED_DISPLAY_HEIGHT +# define OLED_DISPLAY_HEIGHT 128 +# endif +# ifndef OLED_MATRIX_SIZE +# define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH) // 2048 (compile time mathed) +# endif +# ifndef OLED_BLOCK_TYPE +# define OLED_BLOCK_TYPE uint32_t +# endif +# ifndef OLED_BLOCK_COUNT +# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) // 64 (compile time mathed) +# endif +# ifndef OLED_BLOCK_SIZE +# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) // 64 (compile time mathed) +# endif +# ifndef OLED_COM_PINS +# define OLED_COM_PINS COM_PINS_ALT +# endif + +// For 90 degree rotation, we map our internal matrix to oled matrix using fixed arrays +// The OLED writes to it's memory horizontally, starting top left, but our memory starts bottom left in this mode +# ifndef OLED_SOURCE_MAP +# define OLED_SOURCE_MAP \ + { 0, 8, 16, 24, 32, 40, 48, 56 } +# endif +# ifndef OLED_TARGET_MAP +# define OLED_TARGET_MAP \ + { 56, 48, 40, 32, 24, 16, 8, 0 } +# endif #else // defined(OLED_DISPLAY_128X64) // Default 128x32 # ifndef OLED_DISPLAY_WIDTH @@ -191,6 +226,11 @@ typedef enum { // Returns true if the OLED was initialized successfully bool oled_init(oled_rotation_t rotation); +bool oled_cmd(const uint8_t *data, uint16_t size); +bool oled_cmd_P(const uint8_t *data, uint16_t size); +bool oled_write_reg(const uint8_t *data, uint16_t size); +void oled_driver_init(void); + // Called at the start of oled_init, weak function overridable by the user // rotation - the value passed into oled_init // Return new oled_rotation_t if you want to override default rotation @@ -234,6 +274,8 @@ void oled_write_ln(const char *data, bool invert); // Useful for moving the screen in preparation for new drawing void oled_pan(bool left); +void oled_pan_section(bool left, uint16_t y_start, uint16_t y_end, uint16_t x_start, uint16_t x_end); + // Returns a pointer to the requested start index in the buffer plus remaining // buffer length as struct oled_buffer_reader_t oled_read_raw(uint16_t start_index); diff --git a/drivers/oled/oled_driver_i2c.c b/drivers/oled/oled_driver_i2c.c new file mode 100644 index 000000000000..2ea73738afeb --- /dev/null +++ b/drivers/oled/oled_driver_i2c.c @@ -0,0 +1,58 @@ +/* +Copyright 2019 Ryan Caltabiano + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include "i2c_master.h" +#include "oled_driver.h" +#include "oled_common.h" +#include "progmem.h" + +// Address to use for the i2c oled communication +#if !defined(OLED_DISPLAY_ADDRESS) +# define OLED_DISPLAY_ADDRESS 0x3C +#endif + +// Transmit/Write Funcs. +bool oled_cmd(const uint8_t *data, uint16_t size) { + i2c_status_t status = i2c_transmit((OLED_DISPLAY_ADDRESS << 1), data, size, OLED_I2C_TIMEOUT); + + return (status == I2C_STATUS_SUCCESS); +} + +bool oled_cmd_P(const uint8_t *data, uint16_t size) { +#ifdef __AVR__ + i2c_status_t status = i2c_start((OLED_DISPLAY_ADDRESS << 1) | I2C_WRITE, OLED_I2C_TIMEOUT); + + for (uint16_t i = 0; i < size && status >= 0; i++) { + status = i2c_write(pgm_read_byte((const char *)data++), OLED_I2C_TIMEOUT); + if (status) break; + } + + i2c_stop(); + + return (status == I2C_STATUS_SUCCESS); +#else + return oled_cmd(data, size); +#endif +} + +bool oled_write_reg(const uint8_t *data, uint16_t size) { + i2c_status_t status = i2c_writeReg((OLED_DISPLAY_ADDRESS << 1), I2C_DATA, data, size, OLED_I2C_TIMEOUT); + return (status == I2C_STATUS_SUCCESS); +} + +void oled_driver_init(void) { + i2c_init(); +} diff --git a/drivers/oled/oled_driver_spi.c b/drivers/oled/oled_driver_spi.c new file mode 100644 index 000000000000..31f538e882a4 --- /dev/null +++ b/drivers/oled/oled_driver_spi.c @@ -0,0 +1,85 @@ +/* +Copyright 2019 Ryan Caltabiano + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "spi_master.h" +#include "oled_driver.h" +#include "oled_common.h" +#include "progmem.h" +#include "wait.h" + +#ifndef OLED_DC_PIN +# error "The OLED driver in SPI needs a D/C pin defined" +#endif +#ifndef OLED_CS_PIN +# error "The OLED driver in SPI needs a CS pin defined" +#endif +#ifndef OLED_SPI_MODE +# define OLED_SPI_MODE 3 +#endif +#ifndef OLED_SPI_DIVISOR +# define OLED_SPI_DIVISOR 2 +#endif + +// offest arrays by 1 so i2c command doesn't need to be removed. + +// Transmit/Write Funcs. +bool oled_cmd(const uint8_t *data, uint16_t size) { + spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR); + // Command Mode + writePinLow(OLED_DC_PIN); + // Send the commands + if (spi_transmit(&data[1], size - 1) != SPI_STATUS_SUCCESS) { + spi_stop(); + return false; + } + spi_stop(); + return true; +} + +bool oled_cmd_P(const uint8_t *data, uint16_t size) { + return oled_cmd(data, size); +} + +bool oled_write_reg(const uint8_t *data, uint16_t size) { + spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR); + // Command Mode + writePinHigh(OLED_DC_PIN); + // Send the commands + if (spi_transmit(data, size) != SPI_STATUS_SUCCESS) { + spi_stop(); + return false; + } + spi_stop(); + return true; +} + +void oled_driver_init(void) { + spi_init(); + setPinOutput(OLED_CS_PIN); + writePinHigh(OLED_CS_PIN); + + setPinOutput(OLED_DC_PIN); + writePinLow(OLED_DC_PIN); +#ifdef OLED_RST_PIN + /* Reset device */ + setPinOutput(OLED_RST_PIN); + writePinLow(OLED_RST_PIN); + wait_ms(20); + writePinHigh(OLED_RST_PIN); + wait_ms(20); +#endif +} diff --git a/drivers/oled/ssd1306_sh1106.c b/drivers/oled/ssd1306_sh1106.c deleted file mode 100644 index 342920572e3a..000000000000 --- a/drivers/oled/ssd1306_sh1106.c +++ /dev/null @@ -1,795 +0,0 @@ -/* -Copyright 2019 Ryan Caltabiano - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ -#include "i2c_master.h" -#include "oled_driver.h" -#include OLED_FONT_H -#include "timer.h" -#include "print.h" - -#include - -#include "progmem.h" - -#include "keyboard.h" - -// Used commands from spec sheet: https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf -// for SH1106: https://www.velleman.eu/downloads/29/infosheets/sh1106_datasheet.pdf - -// Fundamental Commands -#define CONTRAST 0x81 -#define DISPLAY_ALL_ON 0xA5 -#define DISPLAY_ALL_ON_RESUME 0xA4 -#define NORMAL_DISPLAY 0xA6 -#define INVERT_DISPLAY 0xA7 -#define DISPLAY_ON 0xAF -#define DISPLAY_OFF 0xAE -#define NOP 0xE3 - -// Scrolling Commands -#define ACTIVATE_SCROLL 0x2F -#define DEACTIVATE_SCROLL 0x2E -#define SCROLL_RIGHT 0x26 -#define SCROLL_LEFT 0x27 -#define SCROLL_RIGHT_UP 0x29 -#define SCROLL_LEFT_UP 0x2A - -// Addressing Setting Commands -#define MEMORY_MODE 0x20 -#define COLUMN_ADDR 0x21 -#define PAGE_ADDR 0x22 -#define PAM_SETCOLUMN_LSB 0x00 -#define PAM_SETCOLUMN_MSB 0x10 -#define PAM_PAGE_ADDR 0xB0 // 0xb0 -- 0xb7 - -// Hardware Configuration Commands -#define DISPLAY_START_LINE 0x40 -#define SEGMENT_REMAP 0xA0 -#define SEGMENT_REMAP_INV 0xA1 -#define MULTIPLEX_RATIO 0xA8 -#define COM_SCAN_INC 0xC0 -#define COM_SCAN_DEC 0xC8 -#define DISPLAY_OFFSET 0xD3 -#define COM_PINS 0xDA -#define COM_PINS_SEQ 0x02 -#define COM_PINS_ALT 0x12 -#define COM_PINS_SEQ_LR 0x22 -#define COM_PINS_ALT_LR 0x32 - -// Timing & Driving Commands -#define DISPLAY_CLOCK 0xD5 -#define PRE_CHARGE_PERIOD 0xD9 -#define VCOM_DETECT 0xDB - -// Advance Graphic Commands -#define FADE_BLINK 0x23 -#define ENABLE_FADE 0x20 -#define ENABLE_BLINK 0x30 - -// Charge Pump Commands -#define CHARGE_PUMP 0x8D - -// Misc defines -#ifndef OLED_BLOCK_COUNT -# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) -#endif -#ifndef OLED_BLOCK_SIZE -# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) -#endif - -#define OLED_ALL_BLOCKS_MASK (((((OLED_BLOCK_TYPE)1 << (OLED_BLOCK_COUNT - 1)) - 1) << 1) | 1) - -// i2c defines -#define I2C_CMD 0x00 -#define I2C_DATA 0x40 -#if defined(__AVR__) -# define I2C_TRANSMIT_P(data) i2c_transmit_P((OLED_DISPLAY_ADDRESS << 1), &data[0], sizeof(data), OLED_I2C_TIMEOUT) -#else // defined(__AVR__) -# define I2C_TRANSMIT_P(data) i2c_transmit((OLED_DISPLAY_ADDRESS << 1), &data[0], sizeof(data), OLED_I2C_TIMEOUT) -#endif // defined(__AVR__) -#define I2C_TRANSMIT(data) i2c_transmit((OLED_DISPLAY_ADDRESS << 1), &data[0], sizeof(data), OLED_I2C_TIMEOUT) -#define I2C_WRITE_REG(mode, data, size) i2c_writeReg((OLED_DISPLAY_ADDRESS << 1), mode, data, size, OLED_I2C_TIMEOUT) - -#define HAS_FLAGS(bits, flags) ((bits & flags) == flags) - -// Display buffer's is the same as the OLED memory layout -// this is so we don't end up with rounding errors with -// parts of the display unusable or don't get cleared correctly -// and also allows for drawing & inverting -uint8_t oled_buffer[OLED_MATRIX_SIZE]; -uint8_t * oled_cursor; -OLED_BLOCK_TYPE oled_dirty = 0; -bool oled_initialized = false; -bool oled_active = false; -bool oled_scrolling = false; -bool oled_inverted = false; -uint8_t oled_brightness = OLED_BRIGHTNESS; -oled_rotation_t oled_rotation = 0; -uint8_t oled_rotation_width = 0; -uint8_t oled_scroll_speed = 0; // this holds the speed after being remapped to ssd1306 internal values -uint8_t oled_scroll_start = 0; -uint8_t oled_scroll_end = 7; -#if OLED_TIMEOUT > 0 -uint32_t oled_timeout; -#endif -#if OLED_SCROLL_TIMEOUT > 0 -uint32_t oled_scroll_timeout; -#endif -#if OLED_UPDATE_INTERVAL > 0 -uint16_t oled_update_timeout; -#endif - -// Internal variables to reduce math instructions - -#if defined(__AVR__) -// identical to i2c_transmit, but for PROGMEM since all initialization is in PROGMEM arrays currently -// probably should move this into i2c_master... -static i2c_status_t i2c_transmit_P(uint8_t address, const uint8_t *data, uint16_t length, uint16_t timeout) { - i2c_status_t status = i2c_start(address | I2C_WRITE, timeout); - - for (uint16_t i = 0; i < length && status >= 0; i++) { - status = i2c_write(pgm_read_byte((const char *)data++), timeout); - if (status) break; - } - - i2c_stop(); - - return status; -} -#endif - -// Flips the rendering bits for a character at the current cursor position -static void InvertCharacter(uint8_t *cursor) { - const uint8_t *end = cursor + OLED_FONT_WIDTH; - while (cursor < end) { - *cursor = ~(*cursor); - cursor++; - } -} - -bool oled_init(oled_rotation_t rotation) { -#if defined(USE_I2C) && defined(SPLIT_KEYBOARD) - if (!is_keyboard_master()) { - return true; - } -#endif - - oled_rotation = oled_init_user(oled_init_kb(rotation)); - if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { - oled_rotation_width = OLED_DISPLAY_WIDTH; - } else { - oled_rotation_width = OLED_DISPLAY_HEIGHT; - } - i2c_init(); - - static const uint8_t PROGMEM display_setup1[] = { - I2C_CMD, - DISPLAY_OFF, - DISPLAY_CLOCK, - 0x80, - MULTIPLEX_RATIO, - OLED_DISPLAY_HEIGHT - 1, - DISPLAY_OFFSET, - 0x00, - DISPLAY_START_LINE | 0x00, - CHARGE_PUMP, - 0x14, -#if (OLED_IC != OLED_IC_SH1106) - // MEMORY_MODE is unsupported on SH1106 (Page Addressing only) - MEMORY_MODE, - 0x00, // Horizontal addressing mode -#endif - }; - if (I2C_TRANSMIT_P(display_setup1) != I2C_STATUS_SUCCESS) { - print("oled_init cmd set 1 failed\n"); - return false; - } - - if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_180)) { - static const uint8_t PROGMEM display_normal[] = {I2C_CMD, SEGMENT_REMAP_INV, COM_SCAN_DEC}; - if (I2C_TRANSMIT_P(display_normal) != I2C_STATUS_SUCCESS) { - print("oled_init cmd normal rotation failed\n"); - return false; - } - } else { - static const uint8_t PROGMEM display_flipped[] = {I2C_CMD, SEGMENT_REMAP, COM_SCAN_INC}; - if (I2C_TRANSMIT_P(display_flipped) != I2C_STATUS_SUCCESS) { - print("display_flipped failed\n"); - return false; - } - } - - static const uint8_t PROGMEM display_setup2[] = {I2C_CMD, COM_PINS, OLED_COM_PINS, CONTRAST, OLED_BRIGHTNESS, PRE_CHARGE_PERIOD, 0xF1, VCOM_DETECT, 0x20, DISPLAY_ALL_ON_RESUME, NORMAL_DISPLAY, DEACTIVATE_SCROLL, DISPLAY_ON}; - if (I2C_TRANSMIT_P(display_setup2) != I2C_STATUS_SUCCESS) { - print("display_setup2 failed\n"); - return false; - } - -#if OLED_TIMEOUT > 0 - oled_timeout = timer_read32() + OLED_TIMEOUT; -#endif -#if OLED_SCROLL_TIMEOUT > 0 - oled_scroll_timeout = timer_read32() + OLED_SCROLL_TIMEOUT; -#endif - - oled_clear(); - oled_initialized = true; - oled_active = true; - oled_scrolling = false; - return true; -} - -__attribute__((weak)) oled_rotation_t oled_init_kb(oled_rotation_t rotation) { - return rotation; -} -__attribute__((weak)) oled_rotation_t oled_init_user(oled_rotation_t rotation) { - return rotation; -} - -void oled_clear(void) { - memset(oled_buffer, 0, sizeof(oled_buffer)); - oled_cursor = &oled_buffer[0]; - oled_dirty = OLED_ALL_BLOCKS_MASK; -} - -static void calc_bounds(uint8_t update_start, uint8_t *cmd_array) { - // Calculate commands to set memory addressing bounds. - uint8_t start_page = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_WIDTH; - uint8_t start_column = OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_WIDTH; -#if (OLED_IC == OLED_IC_SH1106) - // Commands for Page Addressing Mode. Sets starting page and column; has no end bound. - // Column value must be split into high and low nybble and sent as two commands. - cmd_array[0] = PAM_PAGE_ADDR | start_page; - cmd_array[1] = PAM_SETCOLUMN_LSB | ((OLED_COLUMN_OFFSET + start_column) & 0x0f); - cmd_array[2] = PAM_SETCOLUMN_MSB | ((OLED_COLUMN_OFFSET + start_column) >> 4 & 0x0f); - cmd_array[3] = NOP; - cmd_array[4] = NOP; - cmd_array[5] = NOP; -#else - // Commands for use in Horizontal Addressing mode. - cmd_array[1] = start_column; - cmd_array[4] = start_page; - cmd_array[2] = (OLED_BLOCK_SIZE + OLED_DISPLAY_WIDTH - 1) % OLED_DISPLAY_WIDTH + cmd_array[1]; - cmd_array[5] = (OLED_BLOCK_SIZE + OLED_DISPLAY_WIDTH - 1) / OLED_DISPLAY_WIDTH - 1; -#endif -} - -static void calc_bounds_90(uint8_t update_start, uint8_t *cmd_array) { - cmd_array[1] = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_HEIGHT * 8; - cmd_array[4] = OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_HEIGHT; - cmd_array[2] = (OLED_BLOCK_SIZE + OLED_DISPLAY_HEIGHT - 1) / OLED_DISPLAY_HEIGHT * 8 - 1 + cmd_array[1]; - ; - cmd_array[5] = (OLED_BLOCK_SIZE + OLED_DISPLAY_HEIGHT - 1) % OLED_DISPLAY_HEIGHT / 8; -} - -uint8_t crot(uint8_t a, int8_t n) { - const uint8_t mask = 0x7; - n &= mask; - return a << n | a >> (-n & mask); -} - -static void rotate_90(const uint8_t *src, uint8_t *dest) { - for (uint8_t i = 0, shift = 7; i < 8; ++i, --shift) { - uint8_t selector = (1 << i); - for (uint8_t j = 0; j < 8; ++j) { - dest[i] |= crot(src[j] & selector, shift - (int8_t)j); - } - } -} - -void oled_render(void) { - // Do we have work to do? - oled_dirty &= OLED_ALL_BLOCKS_MASK; - if (!oled_dirty || !oled_initialized || oled_scrolling) { - return; - } - - // Turn on display if it is off - oled_on(); - - uint8_t update_start = 0; - uint8_t num_processed = 0; - while (oled_dirty && num_processed++ < OLED_UPDATE_PROCESS_LIMIT) { // render all dirty blocks (up to the configured limit) - // Find next dirty block - while (!(oled_dirty & ((OLED_BLOCK_TYPE)1 << update_start))) { - ++update_start; - } - - // Set column & page position - static uint8_t display_start[] = {I2C_CMD, COLUMN_ADDR, 0, OLED_DISPLAY_WIDTH - 1, PAGE_ADDR, 0, OLED_DISPLAY_HEIGHT / 8 - 1}; - if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { - calc_bounds(update_start, &display_start[1]); // Offset from I2C_CMD byte at the start - } else { - calc_bounds_90(update_start, &display_start[1]); // Offset from I2C_CMD byte at the start - } - - // Send column & page position - if (I2C_TRANSMIT(display_start) != I2C_STATUS_SUCCESS) { - print("oled_render offset command failed\n"); - return; - } - - if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { - // Send render data chunk as is - if (I2C_WRITE_REG(I2C_DATA, &oled_buffer[OLED_BLOCK_SIZE * update_start], OLED_BLOCK_SIZE) != I2C_STATUS_SUCCESS) { - print("oled_render data failed\n"); - return; - } - } else { - // Rotate the render chunks - const static uint8_t source_map[] = OLED_SOURCE_MAP; - const static uint8_t target_map[] = OLED_TARGET_MAP; - - static uint8_t temp_buffer[OLED_BLOCK_SIZE]; - memset(temp_buffer, 0, sizeof(temp_buffer)); - for (uint8_t i = 0; i < sizeof(source_map); ++i) { - rotate_90(&oled_buffer[OLED_BLOCK_SIZE * update_start + source_map[i]], &temp_buffer[target_map[i]]); - } - - // Send render data chunk after rotating - if (I2C_WRITE_REG(I2C_DATA, &temp_buffer[0], OLED_BLOCK_SIZE) != I2C_STATUS_SUCCESS) { - print("oled_render90 data failed\n"); - return; - } - } - - // Clear dirty flag of just rendered block - oled_dirty &= ~((OLED_BLOCK_TYPE)1 << update_start); - } -} - -void oled_set_cursor(uint8_t col, uint8_t line) { - uint16_t index = line * oled_rotation_width + col * OLED_FONT_WIDTH; - - // Out of bounds? - if (index >= OLED_MATRIX_SIZE) { - index = 0; - } - - oled_cursor = &oled_buffer[index]; -} - -void oled_advance_page(bool clearPageRemainder) { - uint16_t index = oled_cursor - &oled_buffer[0]; - uint8_t remaining = oled_rotation_width - (index % oled_rotation_width); - - if (clearPageRemainder) { - // Remaining Char count - remaining = remaining / OLED_FONT_WIDTH; - - // Write empty character until next line - while (remaining--) - oled_write_char(' ', false); - } else { - // Next page index out of bounds? - if (index + remaining >= OLED_MATRIX_SIZE) { - index = 0; - remaining = 0; - } - - oled_cursor = &oled_buffer[index + remaining]; - } -} - -void oled_advance_char(void) { - uint16_t nextIndex = oled_cursor - &oled_buffer[0] + OLED_FONT_WIDTH; - uint8_t remainingSpace = oled_rotation_width - (nextIndex % oled_rotation_width); - - // Do we have enough space on the current line for the next character - if (remainingSpace < OLED_FONT_WIDTH) { - nextIndex += remainingSpace; - } - - // Did we go out of bounds - if (nextIndex >= OLED_MATRIX_SIZE) { - nextIndex = 0; - } - - // Update cursor position - oled_cursor = &oled_buffer[nextIndex]; -} - -// Main handler that writes character data to the display buffer -void oled_write_char(const char data, bool invert) { - // Advance to the next line if newline - if (data == '\n') { - // Old source wrote ' ' until end of line... - oled_advance_page(true); - return; - } - - if (data == '\r') { - oled_advance_page(false); - return; - } - - // copy the current render buffer to check for dirty after - static uint8_t oled_temp_buffer[OLED_FONT_WIDTH]; - memcpy(&oled_temp_buffer, oled_cursor, OLED_FONT_WIDTH); - - _Static_assert(sizeof(font) >= ((OLED_FONT_END + 1 - OLED_FONT_START) * OLED_FONT_WIDTH), "OLED_FONT_END references outside array"); - - // set the reder buffer data - uint8_t cast_data = (uint8_t)data; // font based on unsigned type for index - if (cast_data < OLED_FONT_START || cast_data > OLED_FONT_END) { - memset(oled_cursor, 0x00, OLED_FONT_WIDTH); - } else { - const uint8_t *glyph = &font[(cast_data - OLED_FONT_START) * OLED_FONT_WIDTH]; - memcpy_P(oled_cursor, glyph, OLED_FONT_WIDTH); - } - - // Invert if needed - if (invert) { - InvertCharacter(oled_cursor); - } - - // Dirty check - if (memcmp(&oled_temp_buffer, oled_cursor, OLED_FONT_WIDTH)) { - uint16_t index = oled_cursor - &oled_buffer[0]; - oled_dirty |= ((OLED_BLOCK_TYPE)1 << (index / OLED_BLOCK_SIZE)); - // Edgecase check if the written data spans the 2 chunks - oled_dirty |= ((OLED_BLOCK_TYPE)1 << ((index + OLED_FONT_WIDTH - 1) / OLED_BLOCK_SIZE)); - } - - // Finally move to the next char - oled_advance_char(); -} - -void oled_write(const char *data, bool invert) { - const char *end = data + strlen(data); - while (data < end) { - oled_write_char(*data, invert); - data++; - } -} - -void oled_write_ln(const char *data, bool invert) { - oled_write(data, invert); - oled_advance_page(true); -} - -void oled_pan(bool left) { - uint16_t i = 0; - for (uint16_t y = 0; y < OLED_DISPLAY_HEIGHT / 8; y++) { - if (left) { - for (uint16_t x = 0; x < OLED_DISPLAY_WIDTH - 1; x++) { - i = y * OLED_DISPLAY_WIDTH + x; - oled_buffer[i] = oled_buffer[i + 1]; - } - } else { - for (uint16_t x = OLED_DISPLAY_WIDTH - 1; x > 0; x--) { - i = y * OLED_DISPLAY_WIDTH + x; - oled_buffer[i] = oled_buffer[i - 1]; - } - } - } - oled_dirty = OLED_ALL_BLOCKS_MASK; -} - -oled_buffer_reader_t oled_read_raw(uint16_t start_index) { - if (start_index > OLED_MATRIX_SIZE) start_index = OLED_MATRIX_SIZE; - oled_buffer_reader_t ret_reader; - ret_reader.current_element = &oled_buffer[start_index]; - ret_reader.remaining_element_count = OLED_MATRIX_SIZE - start_index; - return ret_reader; -} - -void oled_write_raw_byte(const char data, uint16_t index) { - if (index > OLED_MATRIX_SIZE) index = OLED_MATRIX_SIZE; - if (oled_buffer[index] == data) return; - oled_buffer[index] = data; - oled_dirty |= ((OLED_BLOCK_TYPE)1 << (index / OLED_BLOCK_SIZE)); -} - -void oled_write_raw(const char *data, uint16_t size) { - uint16_t cursor_start_index = oled_cursor - &oled_buffer[0]; - if ((size + cursor_start_index) > OLED_MATRIX_SIZE) size = OLED_MATRIX_SIZE - cursor_start_index; - for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) { - uint8_t c = *data++; - if (oled_buffer[i] == c) continue; - oled_buffer[i] = c; - oled_dirty |= ((OLED_BLOCK_TYPE)1 << (i / OLED_BLOCK_SIZE)); - } -} - -void oled_write_pixel(uint8_t x, uint8_t y, bool on) { - if (x >= oled_rotation_width) { - return; - } - uint16_t index = x + (y / 8) * oled_rotation_width; - if (index >= OLED_MATRIX_SIZE) { - return; - } - uint8_t data = oled_buffer[index]; - if (on) { - data |= (1 << (y % 8)); - } else { - data &= ~(1 << (y % 8)); - } - if (oled_buffer[index] != data) { - oled_buffer[index] = data; - oled_dirty |= ((OLED_BLOCK_TYPE)1 << (index / OLED_BLOCK_SIZE)); - } -} - -#if defined(__AVR__) -void oled_write_P(const char *data, bool invert) { - uint8_t c = pgm_read_byte(data); - while (c != 0) { - oled_write_char(c, invert); - c = pgm_read_byte(++data); - } -} - -void oled_write_ln_P(const char *data, bool invert) { - oled_write_P(data, invert); - oled_advance_page(true); -} - -void oled_write_raw_P(const char *data, uint16_t size) { - uint16_t cursor_start_index = oled_cursor - &oled_buffer[0]; - if ((size + cursor_start_index) > OLED_MATRIX_SIZE) size = OLED_MATRIX_SIZE - cursor_start_index; - for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) { - uint8_t c = pgm_read_byte(data++); - if (oled_buffer[i] == c) continue; - oled_buffer[i] = c; - oled_dirty |= ((OLED_BLOCK_TYPE)1 << (i / OLED_BLOCK_SIZE)); - } -} -#endif // defined(__AVR__) - -bool oled_on(void) { - if (!oled_initialized) { - return oled_active; - } - -#if OLED_TIMEOUT > 0 - oled_timeout = timer_read32() + OLED_TIMEOUT; -#endif - - static const uint8_t PROGMEM display_on[] = -#ifdef OLED_FADE_OUT - {I2C_CMD, FADE_BLINK, 0x00}; -#else - {I2C_CMD, DISPLAY_ON}; -#endif - - if (!oled_active) { - if (I2C_TRANSMIT_P(display_on) != I2C_STATUS_SUCCESS) { - print("oled_on cmd failed\n"); - return oled_active; - } - oled_active = true; - } - return oled_active; -} - -bool oled_off(void) { - if (!oled_initialized) { - return !oled_active; - } - - static const uint8_t PROGMEM display_off[] = -#ifdef OLED_FADE_OUT - {I2C_CMD, FADE_BLINK, ENABLE_FADE | OLED_FADE_OUT_INTERVAL}; -#else - {I2C_CMD, DISPLAY_OFF}; -#endif - - if (oled_active) { - if (I2C_TRANSMIT_P(display_off) != I2C_STATUS_SUCCESS) { - print("oled_off cmd failed\n"); - return oled_active; - } - oled_active = false; - } - return !oled_active; -} - -bool is_oled_on(void) { - return oled_active; -} - -uint8_t oled_set_brightness(uint8_t level) { - if (!oled_initialized) { - return oled_brightness; - } - - uint8_t set_contrast[] = {I2C_CMD, CONTRAST, level}; - if (oled_brightness != level) { - if (I2C_TRANSMIT(set_contrast) != I2C_STATUS_SUCCESS) { - print("set_brightness cmd failed\n"); - return oled_brightness; - } - oled_brightness = level; - } - return oled_brightness; -} - -uint8_t oled_get_brightness(void) { - return oled_brightness; -} - -// Set the specific 8 lines rows of the screen to scroll. -// 0 is the default for start, and 7 for end, which is the entire -// height of the screen. For 128x32 screens, rows 4-7 are not used. -void oled_scroll_set_area(uint8_t start_line, uint8_t end_line) { - oled_scroll_start = start_line; - oled_scroll_end = end_line; -} - -void oled_scroll_set_speed(uint8_t speed) { - // Sets the speed for scrolling... does not take effect - // until scrolling is either started or restarted - // the ssd1306 supports 8 speeds - // FrameRate2 speed = 7 - // FrameRate3 speed = 4 - // FrameRate4 speed = 5 - // FrameRate5 speed = 0 - // FrameRate25 speed = 6 - // FrameRate64 speed = 1 - // FrameRate128 speed = 2 - // FrameRate256 speed = 3 - // for ease of use these are remaped here to be in order - static const uint8_t scroll_remap[8] = {7, 4, 5, 0, 6, 1, 2, 3}; - oled_scroll_speed = scroll_remap[speed]; -} - -bool oled_scroll_right(void) { - if (!oled_initialized) { - return oled_scrolling; - } - - // Dont enable scrolling if we need to update the display - // This prevents scrolling of bad data from starting the scroll too early after init - if (!oled_dirty && !oled_scrolling) { - uint8_t display_scroll_right[] = {I2C_CMD, SCROLL_RIGHT, 0x00, oled_scroll_start, oled_scroll_speed, oled_scroll_end, 0x00, 0xFF, ACTIVATE_SCROLL}; - if (I2C_TRANSMIT(display_scroll_right) != I2C_STATUS_SUCCESS) { - print("oled_scroll_right cmd failed\n"); - return oled_scrolling; - } - oled_scrolling = true; - } - return oled_scrolling; -} - -bool oled_scroll_left(void) { - if (!oled_initialized) { - return oled_scrolling; - } - - // Dont enable scrolling if we need to update the display - // This prevents scrolling of bad data from starting the scroll too early after init - if (!oled_dirty && !oled_scrolling) { - uint8_t display_scroll_left[] = {I2C_CMD, SCROLL_LEFT, 0x00, oled_scroll_start, oled_scroll_speed, oled_scroll_end, 0x00, 0xFF, ACTIVATE_SCROLL}; - if (I2C_TRANSMIT(display_scroll_left) != I2C_STATUS_SUCCESS) { - print("oled_scroll_left cmd failed\n"); - return oled_scrolling; - } - oled_scrolling = true; - } - return oled_scrolling; -} - -bool oled_scroll_off(void) { - if (!oled_initialized) { - return !oled_scrolling; - } - - if (oled_scrolling) { - static const uint8_t PROGMEM display_scroll_off[] = {I2C_CMD, DEACTIVATE_SCROLL}; - if (I2C_TRANSMIT_P(display_scroll_off) != I2C_STATUS_SUCCESS) { - print("oled_scroll_off cmd failed\n"); - return oled_scrolling; - } - oled_scrolling = false; - oled_dirty = OLED_ALL_BLOCKS_MASK; - } - return !oled_scrolling; -} - -bool is_oled_scrolling(void) { - return oled_scrolling; -} - -bool oled_invert(bool invert) { - if (!oled_initialized) { - return oled_inverted; - } - - if (invert && !oled_inverted) { - static const uint8_t PROGMEM display_inverted[] = {I2C_CMD, INVERT_DISPLAY}; - if (I2C_TRANSMIT_P(display_inverted) != I2C_STATUS_SUCCESS) { - print("oled_invert cmd failed\n"); - return oled_inverted; - } - oled_inverted = true; - } else if (!invert && oled_inverted) { - static const uint8_t PROGMEM display_normal[] = {I2C_CMD, NORMAL_DISPLAY}; - if (I2C_TRANSMIT_P(display_normal) != I2C_STATUS_SUCCESS) { - print("oled_invert cmd failed\n"); - return oled_inverted; - } - oled_inverted = false; - } - - return oled_inverted; -} - -uint8_t oled_max_chars(void) { - if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { - return OLED_DISPLAY_WIDTH / OLED_FONT_WIDTH; - } - return OLED_DISPLAY_HEIGHT / OLED_FONT_WIDTH; -} - -uint8_t oled_max_lines(void) { - if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { - return OLED_DISPLAY_HEIGHT / OLED_FONT_HEIGHT; - } - return OLED_DISPLAY_WIDTH / OLED_FONT_HEIGHT; -} - -void oled_task(void) { - if (!oled_initialized) { - return; - } - -#if OLED_UPDATE_INTERVAL > 0 - if (timer_elapsed(oled_update_timeout) >= OLED_UPDATE_INTERVAL) { - oled_update_timeout = timer_read(); - oled_set_cursor(0, 0); - oled_task_kb(); - } -#else - oled_set_cursor(0, 0); - oled_task_kb(); -#endif - -#if OLED_SCROLL_TIMEOUT > 0 - if (oled_dirty && oled_scrolling) { - oled_scroll_timeout = timer_read32() + OLED_SCROLL_TIMEOUT; - oled_scroll_off(); - } -#endif - - // Smart render system, no need to check for dirty - oled_render(); - - // Display timeout check -#if OLED_TIMEOUT > 0 - if (oled_active && timer_expired32(timer_read32(), oled_timeout)) { - oled_off(); - } -#endif - -#if OLED_SCROLL_TIMEOUT > 0 - if (!oled_scrolling && timer_expired32(timer_read32(), oled_scroll_timeout)) { -# ifdef OLED_SCROLL_TIMEOUT_RIGHT - oled_scroll_right(); -# else - oled_scroll_left(); -# endif - } -#endif -} - -__attribute__((weak)) bool oled_task_kb(void) { - return oled_task_user(); -} -__attribute__((weak)) bool oled_task_user(void) { - return true; -} diff --git a/keyboards/adafruit/macropad/rules.mk b/keyboards/adafruit/macropad/rules.mk index 23b15d77fcb3..16c6b3c2e1f5 100644 --- a/keyboards/adafruit/macropad/rules.mk +++ b/keyboards/adafruit/macropad/rules.mk @@ -14,7 +14,4 @@ AUDIO_DRIVER = pwm_hardware ENCODER_ENABLE = yes RGB_MATRIX_ENABLE = yes OLED_ENABLE = yes -OLED_DRIVER = custom -# Project specific files -SRC += lib/ssd1306_sh1106.c -QUANTUM_LIB_SRC += spi_master.c +OLED_DRIVER = SPI diff --git a/keyboards/bastardkb/dilemma/3x5_2/keymaps/drashna/rules.mk b/keyboards/bastardkb/dilemma/3x5_2/keymaps/drashna/rules.mk index 41d73bc49ae1..a1be9cb8fd14 100644 --- a/keyboards/bastardkb/dilemma/3x5_2/keymaps/drashna/rules.mk +++ b/keyboards/bastardkb/dilemma/3x5_2/keymaps/drashna/rules.mk @@ -5,4 +5,3 @@ CONSOLE_ENABLE = yes KEYLOGGER_ENABLE = no WPM_ENABLE = yes OLED_ENABLE = yes -OLED_DRIVER = custom diff --git a/keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/config.h b/keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/config.h index f159a69174e8..217022e04900 100644 --- a/keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/config.h +++ b/keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/config.h @@ -18,8 +18,9 @@ #define DEBOUNCE 45 -#ifdef OLED_DRIVER_SH1107 -# undef OLED_DISPLAY_128X64 +#ifdef OLED_ENABLE +# define OLED_IC OLED_IC_SH1107 +# define OLED_DISPLAY_128X128 #endif #define CHARYBDIS_MINIMUM_DEFAULT_DPI 1200 diff --git a/keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/rules.mk b/keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/rules.mk index acec28fa6e23..85abb142396c 100644 --- a/keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/rules.mk +++ b/keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/rules.mk @@ -5,7 +5,6 @@ CUSTOM_BOOTMAGIC_ENABLE = no CUSTOM_UNICODE_ENABLE = no HAPTIC_ENABLE = no OLED_ENABLE = no -OLED_DRIVER = custom RGBLIGHT_ENABLE = no SWAP_HANDS_ENABLE = no TAP_DANCE_ENABLE = no diff --git a/keyboards/splitkb/kyria/keymaps/drashna/config.h b/keyboards/splitkb/kyria/keymaps/drashna/config.h index 2d1fbdcb02d0..43d4f070f393 100644 --- a/keyboards/splitkb/kyria/keymaps/drashna/config.h +++ b/keyboards/splitkb/kyria/keymaps/drashna/config.h @@ -19,10 +19,9 @@ #define EE_HANDS #ifdef OLED_ENABLE -# ifdef OLED_DRIVER_SH1107 -# undef OLED_DISPLAY_128X64 -# define OLED_DISPLAY_128X128 -# endif +# define OLED_IC OLED_IC_SH1107 +# undef OLED_DISPLAY_128X64 +# define OLED_DISPLAY_128X128 #endif #ifdef RGBLIGHT_ENABLE diff --git a/keyboards/splitkb/kyria/keymaps/drashna/keymap.c b/keyboards/splitkb/kyria/keymaps/drashna/keymap.c index 178d3f2480dc..da5b10bfedc5 100644 --- a/keyboards/splitkb/kyria/keymaps/drashna/keymap.c +++ b/keyboards/splitkb/kyria/keymaps/drashna/keymap.c @@ -126,11 +126,7 @@ void render_oled_title(bool side) { } oled_rotation_t oled_init_keymap(oled_rotation_t rotation) { -# ifdef OLED_DRIVER_SH1107 return OLED_ROTATION_0; -# else - return OLED_ROTATION_180; -# endif } void oled_render_large_display(bool side) { diff --git a/keyboards/splitkb/kyria/keymaps/drashna/rules.mk b/keyboards/splitkb/kyria/keymaps/drashna/rules.mk index 882a3fba092a..8ff7b010a18b 100644 --- a/keyboards/splitkb/kyria/keymaps/drashna/rules.mk +++ b/keyboards/splitkb/kyria/keymaps/drashna/rules.mk @@ -20,7 +20,6 @@ ifeq ($(strip $(KEYBOARD)), splitkb/kyria/rev1/proton_c) ENCODER_MAP_ENABLE = yes AUTOCORRECT_ENABLE = yes CAPS_WORD_ENABLE = yes - OLED_DRIVER = custom else LTO_ENABLE = yes BOOTLOADER = qmk-hid diff --git a/users/drashna/oled/oled.mk b/users/drashna/oled/oled.mk new file mode 100644 index 000000000000..25dca0a3badd --- /dev/null +++ b/users/drashna/oled/oled.mk @@ -0,0 +1,9 @@ +CUSTOM_OLED_DRIVER ?= yes +ifeq ($(strip $(CUSTOM_OLED_DRIVER)), yes) + OPT_DEFS += -DCUSTOM_OLED_DRIVER + SRC += $(USER_PATH)/oled/oled_stuff.c +endif +ifeq ($(strip $(OLED_DISPLAY_TEST)), yes) + OPT_DEFS += -DOLED_DISPLAY_TEST +endif +DEFERRED_EXEC_ENABLE = yes diff --git a/users/drashna/oled/oled_config.h b/users/drashna/oled/oled_config.h index c46c0c39ce2f..d8787259f488 100644 --- a/users/drashna/oled/oled_config.h +++ b/users/drashna/oled/oled_config.h @@ -40,24 +40,3 @@ #define OLED_LOGO_SCIFI // # define OLED_LOGO_SETS3N // # define OLED_LOGO_SKEEB - -#ifdef OLED_DRIVER_SH1107 -# define OLED_DISPLAY_CUSTOM -# define OLED_IC_SH1107 2 -# define OLED_DISPLAY_128X128 -# define OLED_DISPLAY_WIDTH 128 -# define OLED_DISPLAY_HEIGHT 128 -# define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH) -# define OLED_BLOCK_TYPE uint32_t -# define OLED_SOURCE_MAP \ - { 0, 8, 16, 24, 32, 40, 48, 56 } -# define OLED_TARGET_MAP \ - { 56, 48, 40, 32, 24, 16, 8, 0 } -# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) -# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) -# define OLED_COM_PINS COM_PINS_ALT -# define OLED_IC OLED_IC_SH1107 -# ifndef OLED_BRIGHTNESS -# define OLED_BRIGHTNESS 50 -# endif -#endif From a5db66531d860f0f1d3cd7d24fd0cbc98cecfc16 Mon Sep 17 00:00:00 2001 From: Drashna Jael're Date: Sat, 28 Jan 2023 21:15:06 -0800 Subject: [PATCH 02/26] simplify --- builddefs/common_features.mk | 2 - drivers/oled/oled_common.h | 94 ----------------- drivers/oled/oled_driver.c | 188 +++++++++++++++++++++++++++++++-- drivers/oled/oled_driver_i2c.c | 58 ---------- drivers/oled/oled_driver_spi.c | 85 --------------- 5 files changed, 181 insertions(+), 246 deletions(-) delete mode 100644 drivers/oled/oled_common.h delete mode 100644 drivers/oled/oled_driver_i2c.c delete mode 100644 drivers/oled/oled_driver_spi.c diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk index c04d57214240..0be221a30aa0 100644 --- a/builddefs/common_features.mk +++ b/builddefs/common_features.mk @@ -752,11 +752,9 @@ ifeq ($(strip $(OLED_ENABLE)), yes) OPT_DEFS += -DOLED_DRIVER_$(strip $(shell echo $(OLED_DRIVER) | tr '[:lower:]' '[:upper:]')) ifeq ($(strip $(OLED_DRIVER)), I2C) - SRC += oled_driver_i2c.c QUANTUM_LIB_SRC += i2c_master.c endif ifeq ($(strip $(OLED_DRIVER)), SPI) - SRC += oled_driver_spi.c QUANTUM_LIB_SRC += spi_master.c endif endif diff --git a/drivers/oled/oled_common.h b/drivers/oled/oled_common.h deleted file mode 100644 index c1233173fd5a..000000000000 --- a/drivers/oled/oled_common.h +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once - -#include "progmem.h" - -// Fundamental Commands -#define CONTRAST 0x81 -#define DISPLAY_ALL_ON 0xA5 -#define DISPLAY_ALL_ON_RESUME 0xA4 -#define NORMAL_DISPLAY 0xA6 -#define INVERT_DISPLAY 0xA7 -#define DISPLAY_ON 0xAF -#define DISPLAY_OFF 0xAE -#define NOP 0xE3 - -// Scrolling Commands -#define ACTIVATE_SCROLL 0x2F -#define DEACTIVATE_SCROLL 0x2E -#define SCROLL_RIGHT 0x26 -#define SCROLL_LEFT 0x27 -#define SCROLL_RIGHT_UP 0x29 -#define SCROLL_LEFT_UP 0x2A - -// Addressing Setting Commands -#define MEMORY_MODE 0x20 -#define COLUMN_ADDR 0x21 -#define PAGE_ADDR 0x22 -#define PAM_SETCOLUMN_LSB 0x00 -#define PAM_SETCOLUMN_MSB 0x10 -#define PAM_PAGE_ADDR 0xB0 // 0xb0 -- 0xb7 - -// Hardware Configuration Commands -#define DISPLAY_START_LINE 0x40 -#define SEGMENT_REMAP 0xA0 -#define SEGMENT_REMAP_INV 0xA1 -#define MULTIPLEX_RATIO 0xA8 -#define COM_SCAN_INC 0xC0 -#define COM_SCAN_DEC 0xC8 -#define DISPLAY_OFFSET 0xD3 -#define COM_PINS 0xDA -#define COM_PINS_SEQ 0x02 -#define COM_PINS_ALT 0x12 -#define COM_PINS_SEQ_LR 0x22 -#define COM_PINS_ALT_LR 0x32 - -// Timing & Driving Commands -#define DISPLAY_CLOCK 0xD5 -#define PRE_CHARGE_PERIOD 0xD9 -#define VCOM_DETECT 0xDB - -// Advance Graphic Commands -#define FADE_BLINK 0x23 -#define ENABLE_FADE 0x20 -#define ENABLE_BLINK 0x30 - -// Charge Pump Commands -#define CHARGE_PUMP 0x8D - -// Commands specific to the SH1107 chip -#define SH1107_DISPLAY_START_LINE 0xDC -#define SH1107_MEMORY_MODE_PAGE 0x20 -#define SH1107_MEMORY_MODE_VERTICAL 0x21 - -// Misc defines -#ifndef OLED_BLOCK_COUNT -# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) -#endif -#ifndef OLED_BLOCK_SIZE -# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) -#endif - -#define OLED_ALL_BLOCKS_MASK (((((OLED_BLOCK_TYPE)1 << (OLED_BLOCK_COUNT - 1)) - 1) << 1) | 1) - -#define OLED_IC_HAS_HORIZONTAL_MODE (OLED_IC == OLED_IC_SSD1306) -#define OLED_IC_COM_PINS_ARE_COLUMNS (OLED_IC == OLED_IC_SH1107) - -#ifndef OLED_COM_PIN_COUNT -# if OLED_IC == OLED_IC_SSD1306 -# define OLED_COM_PIN_COUNT 64 -# elif OLED_IC == OLED_IC_SH1106 -# define OLED_COM_PIN_COUNT 64 -# elif OLED_IC == OLED_IC_SH1107 -# define OLED_COM_PIN_COUNT 128 -# else -# error Invalid OLED_IC value -# endif -#endif - -#ifndef OLED_COM_PIN_OFFSET -# define OLED_COM_PIN_OFFSET 0 -#endif - -// i2c defines -#define I2C_CMD 0x00 -#define I2C_DATA 0x40 diff --git a/drivers/oled/oled_driver.c b/drivers/oled/oled_driver.c index e607d8927618..f1fd65e1ec9c 100644 --- a/drivers/oled/oled_driver.c +++ b/drivers/oled/oled_driver.c @@ -28,6 +28,100 @@ along with this program. If not, see . // for SH1106: https://www.velleman.eu/downloads/29/infosheets/sh1106_datasheet.pdf // for SH1107: https://www.displayfuture.com/Display/datasheet/controller/SH1107.pdf + +#include "progmem.h" + +// Fundamental Commands +#define CONTRAST 0x81 +#define DISPLAY_ALL_ON 0xA5 +#define DISPLAY_ALL_ON_RESUME 0xA4 +#define NORMAL_DISPLAY 0xA6 +#define INVERT_DISPLAY 0xA7 +#define DISPLAY_ON 0xAF +#define DISPLAY_OFF 0xAE +#define NOP 0xE3 + +// Scrolling Commands +#define ACTIVATE_SCROLL 0x2F +#define DEACTIVATE_SCROLL 0x2E +#define SCROLL_RIGHT 0x26 +#define SCROLL_LEFT 0x27 +#define SCROLL_RIGHT_UP 0x29 +#define SCROLL_LEFT_UP 0x2A + +// Addressing Setting Commands +#define MEMORY_MODE 0x20 +#define COLUMN_ADDR 0x21 +#define PAGE_ADDR 0x22 +#define PAM_SETCOLUMN_LSB 0x00 +#define PAM_SETCOLUMN_MSB 0x10 +#define PAM_PAGE_ADDR 0xB0 // 0xb0 -- 0xb7 + +// Hardware Configuration Commands +#define DISPLAY_START_LINE 0x40 +#define SEGMENT_REMAP 0xA0 +#define SEGMENT_REMAP_INV 0xA1 +#define MULTIPLEX_RATIO 0xA8 +#define COM_SCAN_INC 0xC0 +#define COM_SCAN_DEC 0xC8 +#define DISPLAY_OFFSET 0xD3 +#define COM_PINS 0xDA +#define COM_PINS_SEQ 0x02 +#define COM_PINS_ALT 0x12 +#define COM_PINS_SEQ_LR 0x22 +#define COM_PINS_ALT_LR 0x32 + +// Timing & Driving Commands +#define DISPLAY_CLOCK 0xD5 +#define PRE_CHARGE_PERIOD 0xD9 +#define VCOM_DETECT 0xDB + +// Advance Graphic Commands +#define FADE_BLINK 0x23 +#define ENABLE_FADE 0x20 +#define ENABLE_BLINK 0x30 + +// Charge Pump Commands +#define CHARGE_PUMP 0x8D + +// Commands specific to the SH1107 chip +#define SH1107_DISPLAY_START_LINE 0xDC +#define SH1107_MEMORY_MODE_PAGE 0x20 +#define SH1107_MEMORY_MODE_VERTICAL 0x21 + +// Misc defines +#ifndef OLED_BLOCK_COUNT +# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) +#endif +#ifndef OLED_BLOCK_SIZE +# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) +#endif + +#define OLED_ALL_BLOCKS_MASK (((((OLED_BLOCK_TYPE)1 << (OLED_BLOCK_COUNT - 1)) - 1) << 1) | 1) + +#define OLED_IC_HAS_HORIZONTAL_MODE (OLED_IC == OLED_IC_SSD1306) +#define OLED_IC_COM_PINS_ARE_COLUMNS (OLED_IC == OLED_IC_SH1107) + +#ifndef OLED_COM_PIN_COUNT +# if OLED_IC == OLED_IC_SSD1306 +# define OLED_COM_PIN_COUNT 64 +# elif OLED_IC == OLED_IC_SH1106 +# define OLED_COM_PIN_COUNT 64 +# elif OLED_IC == OLED_IC_SH1107 +# define OLED_COM_PIN_COUNT 128 +# else +# error Invalid OLED_IC value +# endif +#endif + +#ifndef OLED_COM_PIN_OFFSET +# define OLED_COM_PIN_OFFSET 0 +#endif + +// i2c defines +#define I2C_CMD 0x00 +#define I2C_DATA 0x40 + #undef ARRAY_SIZE #define ARRAY_SIZE(arr) sizeof(arr)/sizeof(arr[0]) @@ -60,22 +154,102 @@ uint32_t oled_scroll_timeout; uint16_t oled_update_timeout; #endif +#ifdef OLED_DRIVER_SPI +# ifndef OLED_DC_PIN +# error "The OLED driver in SPI needs a D/C pin defined" +# endif +# ifndef OLED_CS_PIN +# error "The OLED driver in SPI needs a CS pin defined" +# endif +# ifndef OLED_SPI_MODE +# define OLED_SPI_MODE 3 +# endif +# ifndef OLED_SPI_DIVISOR +# define OLED_SPI_DIVISOR 2 +# endif +#else +# if !defined(OLED_DISPLAY_ADDRESS) +# define OLED_DISPLAY_ADDRESS 0x3C +# endif +#endif + // Transmit/Write Funcs. -__attribute__((weak)) bool oled_cmd(const uint8_t *data, uint16_t size) { - return false; +bool oled_cmd(const uint8_t *data, uint16_t size) { +#ifdef OLED_DRIVER_SPI + spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR); + // Command Mode + writePinLow(OLED_DC_PIN); + // Send the commands + if (spi_transmit(&data[1], size - 1) != SPI_STATUS_SUCCESS) { + spi_stop(); + return false; + } + spi_stop(); + return true; +#else + i2c_status_t status = i2c_transmit((OLED_DISPLAY_ADDRESS << 1), data, size, OLED_I2C_TIMEOUT); + + return (status == I2C_STATUS_SUCCESS); +#endif } -__attribute__((weak)) bool oled_cmd_P(const uint8_t *data, uint16_t size) { +oled_cmd_P(const uint8_t *data, uint16_t size) { +#if defined(__AVR__) && !defined(OLED_DRIVER_SPI) + i2c_status_t status = i2c_start((OLED_DISPLAY_ADDRESS << 1) | I2C_WRITE, OLED_I2C_TIMEOUT); + + for (uint16_t i = 0; i < size && status >= 0; i++) { + status = i2c_write(pgm_read_byte((const char *)data++), OLED_I2C_TIMEOUT); + if (status) break; + } + + i2c_stop(); + + return (status == I2C_STATUS_SUCCESS); +#else return oled_cmd(data, size); +#endif } -__attribute__((weak)) bool oled_write_reg(const uint8_t *data, uint16_t size) { - return false; -} +bool oled_write_reg(const uint8_t *data, uint16_t size) { +#ifdef OLED_DRIVER_SPI + spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR); + // Command Mode + writePinHigh(OLED_DC_PIN); + // Send the commands + if (spi_transmit(data, size) != SPI_STATUS_SUCCESS) { + spi_stop(); + return false; + } + spi_stop(); +#else + return true; -__attribute__((weak)) void oled_driver_init(void) {} + i2c_status_t status = i2c_writeReg((OLED_DISPLAY_ADDRESS << 1), I2C_DATA, data, size, OLED_I2C_TIMEOUT); + return (status == I2C_STATUS_SUCCESS); +#endif +} +void oled_driver_init(void) { +#ifdef OLED_DRIVER_SPI + spi_init(); + setPinOutput(OLED_CS_PIN); + writePinHigh(OLED_CS_PIN); + + setPinOutput(OLED_DC_PIN); + writePinLow(OLED_DC_PIN); +# ifdef OLED_RST_PIN + /* Reset device */ + setPinOutput(OLED_RST_PIN); + writePinLow(OLED_RST_PIN); + wait_ms(20); + writePinHigh(OLED_RST_PIN); + wait_ms(20); +# endif +#else + i2c_init(); +#endif +} // Flips the rendering bits for a character at the current cursor position static void InvertCharacter(uint8_t *cursor) { diff --git a/drivers/oled/oled_driver_i2c.c b/drivers/oled/oled_driver_i2c.c deleted file mode 100644 index 2ea73738afeb..000000000000 --- a/drivers/oled/oled_driver_i2c.c +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright 2019 Ryan Caltabiano - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ -#include "i2c_master.h" -#include "oled_driver.h" -#include "oled_common.h" -#include "progmem.h" - -// Address to use for the i2c oled communication -#if !defined(OLED_DISPLAY_ADDRESS) -# define OLED_DISPLAY_ADDRESS 0x3C -#endif - -// Transmit/Write Funcs. -bool oled_cmd(const uint8_t *data, uint16_t size) { - i2c_status_t status = i2c_transmit((OLED_DISPLAY_ADDRESS << 1), data, size, OLED_I2C_TIMEOUT); - - return (status == I2C_STATUS_SUCCESS); -} - -bool oled_cmd_P(const uint8_t *data, uint16_t size) { -#ifdef __AVR__ - i2c_status_t status = i2c_start((OLED_DISPLAY_ADDRESS << 1) | I2C_WRITE, OLED_I2C_TIMEOUT); - - for (uint16_t i = 0; i < size && status >= 0; i++) { - status = i2c_write(pgm_read_byte((const char *)data++), OLED_I2C_TIMEOUT); - if (status) break; - } - - i2c_stop(); - - return (status == I2C_STATUS_SUCCESS); -#else - return oled_cmd(data, size); -#endif -} - -bool oled_write_reg(const uint8_t *data, uint16_t size) { - i2c_status_t status = i2c_writeReg((OLED_DISPLAY_ADDRESS << 1), I2C_DATA, data, size, OLED_I2C_TIMEOUT); - return (status == I2C_STATUS_SUCCESS); -} - -void oled_driver_init(void) { - i2c_init(); -} diff --git a/drivers/oled/oled_driver_spi.c b/drivers/oled/oled_driver_spi.c deleted file mode 100644 index 31f538e882a4..000000000000 --- a/drivers/oled/oled_driver_spi.c +++ /dev/null @@ -1,85 +0,0 @@ -/* -Copyright 2019 Ryan Caltabiano - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "spi_master.h" -#include "oled_driver.h" -#include "oled_common.h" -#include "progmem.h" -#include "wait.h" - -#ifndef OLED_DC_PIN -# error "The OLED driver in SPI needs a D/C pin defined" -#endif -#ifndef OLED_CS_PIN -# error "The OLED driver in SPI needs a CS pin defined" -#endif -#ifndef OLED_SPI_MODE -# define OLED_SPI_MODE 3 -#endif -#ifndef OLED_SPI_DIVISOR -# define OLED_SPI_DIVISOR 2 -#endif - -// offest arrays by 1 so i2c command doesn't need to be removed. - -// Transmit/Write Funcs. -bool oled_cmd(const uint8_t *data, uint16_t size) { - spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR); - // Command Mode - writePinLow(OLED_DC_PIN); - // Send the commands - if (spi_transmit(&data[1], size - 1) != SPI_STATUS_SUCCESS) { - spi_stop(); - return false; - } - spi_stop(); - return true; -} - -bool oled_cmd_P(const uint8_t *data, uint16_t size) { - return oled_cmd(data, size); -} - -bool oled_write_reg(const uint8_t *data, uint16_t size) { - spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR); - // Command Mode - writePinHigh(OLED_DC_PIN); - // Send the commands - if (spi_transmit(data, size) != SPI_STATUS_SUCCESS) { - spi_stop(); - return false; - } - spi_stop(); - return true; -} - -void oled_driver_init(void) { - spi_init(); - setPinOutput(OLED_CS_PIN); - writePinHigh(OLED_CS_PIN); - - setPinOutput(OLED_DC_PIN); - writePinLow(OLED_DC_PIN); -#ifdef OLED_RST_PIN - /* Reset device */ - setPinOutput(OLED_RST_PIN); - writePinLow(OLED_RST_PIN); - wait_ms(20); - writePinHigh(OLED_RST_PIN); - wait_ms(20); -#endif -} From 55741e28456ece34a5191b25a01d0e18f5656337 Mon Sep 17 00:00:00 2001 From: Drashna Jael're Date: Tue, 21 Mar 2023 02:16:14 -0700 Subject: [PATCH 03/26] Add docs and defaults --- docs/feature_oled_driver.md | 66 +++++++++++++++------- drivers/oled/oled_driver.h | 109 ++++++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+), 19 deletions(-) diff --git a/docs/feature_oled_driver.md b/docs/feature_oled_driver.md index dea9cb807495..96a887e6f6e7 100644 --- a/docs/feature_oled_driver.md +++ b/docs/feature_oled_driver.md @@ -2,15 +2,18 @@ ## Supported Hardware -OLED modules using SSD1306 or SH1106 driver ICs, communicating over I2C. +OLED modules using SSD1306, SH1106 or SH1107 driver ICs, communicating over I2C or SPI. Tested combinations: -|IC |Size |Platform|Notes | -|---------|------|--------|------------------------| -|SSD1306 |128x32|AVR |Primary support | -|SSD1306 |128x64|AVR |Verified working | -|SSD1306 |128x32|Arm | | -|SH1106 |128x64|AVR |No rotation or scrolling| +|IC |Size |Platform|Notes | +|---------|-------|--------|------------------------| +|SSD1306 |128x32 |AVR |Primary support | +|SSD1306 |128x64 |AVR |Verified working | +|SSD1306 |128x32 |Arm | | +|SH1106 |128x64 |AVR |No scrolling | +|SH1107 |64x128 |AVR |No scrolling | +|SH1107 |64x128 |Arm |No scrolling | +|SH1107 |128x128|Arm |No scrolling | Hardware configurations using Arm-based microcontrollers or different sizes of OLED modules may be compatible, but are untested. @@ -23,13 +26,14 @@ OLED_ENABLE = yes ``` ## OLED type -|OLED Driver |Supported Device | -|-------------------|---------------------------| -|SSD1306 (default) |For both SSD1306 and SH1106| +|OLED Driver | +|-------------------| +|I2C (default) | +|SPI | e.g. ```make -OLED_DRIVER = SSD1306 +OLED_DRIVER = I2C ``` Then in your `keymap.c` file, implement the OLED task call. This example assumes your keymap has three layers named `_QWERTY`, `_FN` and `_ADJ`: @@ -161,7 +165,6 @@ These configuration options should be placed in `config.h`. Example: |Define |Default |Description | |---------------------------|-----------------|--------------------------------------------------------------------------------------------------------------------------| -|`OLED_DISPLAY_ADDRESS` |`0x3C` |The i2c address of the OLED Display | |`OLED_FONT_H` |`"glcdfont.c"` |The font code file to use for custom fonts | |`OLED_FONT_START` |`0` |The starting character index for custom fonts | |`OLED_FONT_END` |`223` |The ending character index for custom fonts | @@ -172,19 +175,38 @@ These configuration options should be placed in `config.h`. Example: |`OLED_FADE_OUT_INTERVAL` |`0` |The speed of fade out animation, from 0 to 15. Larger values are slower. | |`OLED_SCROLL_TIMEOUT` |`0` |Scrolls the OLED screen after 0ms of OLED inactivity. Helps reduce OLED Burn-in. Set to 0 to disable. | |`OLED_SCROLL_TIMEOUT_RIGHT`|*Not defined* |Scroll timeout direction is right when defined, left when undefined. | -|`OLED_IC` |`OLED_IC_SSD1306`|Set to `OLED_IC_SH1106` if you're using the SH1106 OLED controller. | -|`OLED_COLUMN_OFFSET` |`0` |(SH1106 only.) Shift output to the right this many pixels.
Useful for 128x64 displays centered on a 132x64 SH1106 IC.| +|`OLED_IC` |`OLED_IC_SSD1306`|Set to `OLED_IC_SH1106` or `OLED_IC_SH1107` if the corresponding controller chip is used. | +|`OLED_COLUMN_OFFSET` |`0` |Shift output to the right this many pixels.
Useful for 128x64 displays centered on a 132x64 SH1106 IC. | |`OLED_BRIGHTNESS` |`255` |The default brightness level of the OLED, from 0 to 255. | |`OLED_UPDATE_INTERVAL` |`0` |Set the time interval for updating the OLED display in ms. This will improve the matrix scan rate. | +|Define |Default |Description | +|---------------------------|-----------------|--------------------------------------------------------------------------------------------------------------------------| +|`OLED_DISPLAY_ADDRESS` |`0x3C` |The i2c address of the OLED Display | + +|Define |Default |Description | +|---------------------------|-----------------|--------------------------------------------------------------------------------------------------------------------------| +|`OLED_DC_PIN` | Required |The pin used for the DC connection of the OLED Display. | +|`OLED_CS_PIN` | Required |The pin used for the CS connection of the OLED Display. | +|`OLED_SPI_MODE` |`3` (default) |The SPI Mode for the OLED Display (not typically changed). | +|`OLED_SPI_DIVISOR` |`2` (default) |The SPI Multiplier to use for the OLED Display. | + + ## 128x64 & Custom sized OLED Displays The default display size for this feature is 128x32 and all necessary defines are precalculated with that in mind. We have added a define, `OLED_DISPLAY_128X64`, to switch all the values to be used in a 128x64 display, as well as added a custom define, `OLED_DISPLAY_CUSTOM`, that allows you to provide the necessary values to the driver. +|Define |Default |Description | +|----------------------|---------------|---------------------------------------------------------------------------------------------------------------------------------------| +|`OLED_DISPLAY_128X64` |*Not defined* |Changes the display defines for use with 128x64 displays. | +|`OLED_DISPLAY_64X32` |*Not defined* |Changes the display defines for use with 64x32 displays. | +|`OLED_DISPLAY_64X48` |*Not defined* |Changes the display defines for use with 64x48 displays. | +|`OLED_DISPLAY_64X128` |*Not defined* |Changes the display defines for use with 64x128 displays. | +|`OLED_DISPLAY_128X128`|*Not defined* |Changes the display defines for use with 128x128 displays. | +|`OLED_DISPLAY_CUSTOM` |*Not defined* |Changes the display defines for use with custom displays.
Requires user to implement the below defines. | + |Define |Default |Description | -|---------------------|---------------|----------------------------------------------------------------------------------------------------------------------------------------| -|`OLED_DISPLAY_128X64`|*Not defined* |Changes the display defines for use with 128x64 displays. | -|`OLED_DISPLAY_CUSTOM`|*Not defined* |Changes the display defines for use with custom displays.
Requires user to implement the below defines. | +| --------------------|---------------|----------------------------------------------------------------------------------------------------------------------------------------| |`OLED_DISPLAY_WIDTH` |`128` |The width of the OLED display. | |`OLED_DISPLAY_HEIGHT`|`32` |The height of the OLED display. | |`OLED_MATRIX_SIZE` |`512` |The local buffer size to allocate.
`(OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH)`. | @@ -192,6 +214,8 @@ These configuration options should be placed in `config.h`. Example: |`OLED_BLOCK_COUNT` |`16` |The number of blocks the display is divided into for dirty rendering.
`(sizeof(OLED_BLOCK_TYPE) * 8)`. | |`OLED_BLOCK_SIZE` |`32` |The size of each block for dirty rendering
`(OLED_MATRIX_SIZE / OLED_BLOCK_COUNT)`. | |`OLED_COM_PINS` |`COM_PINS_SEQ` |How the SSD1306 chip maps it's memory to display.
Options are `COM_PINS_SEQ`, `COM_PINS_ALT`, `COM_PINS_SEQ_LR`, & `COM_PINS_ALT_LR`.| +|`OLED_COM_PIN_COUNT` |*Not defined* |Number of COM pins supported by the controller.
If not defined, the value appropriate for the defined `OLED_IC` is used. | +|`OLED_COM_PIN_OFFSET`|`0` |Number of the first COM pin used by the OLED matrix. | |`OLED_SOURCE_MAP` |`{ 0, ... N }` |Precalculated source array to use for mapping source buffer to target OLED memory in 90 degree rendering. | |`OLED_TARGET_MAP` |`{ 24, ... N }`|Precalculated target array to use for mapping source buffer to target OLED memory in 90 degree rendering. | @@ -210,7 +234,7 @@ typedef enum { } oled_rotation_t; ``` -OLED displays driven by SSD1306 drivers only natively support in hardware 0 degree and 180 degree rendering. This feature is done in software and not free. Using this feature will increase the time to calculate what data to send over i2c to the OLED. If you are strapped for cycles, this can cause keycodes to not register. In testing however, the rendering time on an ATmega32U4 board only went from 2ms to 5ms and keycodes not registering was only noticed once we hit 15ms. +OLED displays driven by SSD1306, SH1106 or SH1107 drivers only natively support in hardware 0 degree and 180 degree rendering. This feature is done in software and not free. Using this feature will increase the time to calculate what data to send over i2c to the OLED. If you are strapped for cycles, this can cause keycodes to not register. In testing however, the rendering time on an ATmega32U4 board only went from 2ms to 5ms and keycodes not registering was only noticed once we hit 15ms. 90 degree rotation is achieved by using bitwise operations to rotate each 8 block of memory and uses two precalculated arrays to remap buffer memory to OLED memory. The memory map defines are precalculated for remap performance and are calculated based on the display height, width, and block size. For example, in the 128x32 implementation with a `uint8_t` block type, we have a 64 byte block size. This gives us eight 8 byte blocks that need to be rotated and rendered. The OLED renders horizontally two 8 byte blocks before moving down a page, e.g: @@ -232,6 +256,8 @@ However the local buffer is stored as if it was Height x Width display instead o So those precalculated arrays just index the memory offsets in the order in which each one iterates its data. +Rotation on SH1106 and SH1107 is noticeably less efficient than on SSD1306, because these controllers do not support the “horizontal addressing mode”, which allows transferring the data for the whole rotated block at once; instead, separate address setup commands for every page in the block are required. The screen refresh time for SH1107 is therefore about 45% higher than for a same size screen with SSD1306 when using STM32 MCUs (on AVR the slowdown is about 20%, because the code which actually rotates the bitmap consumes more time). + ## OLED API ```c @@ -386,7 +412,9 @@ uint8_t oled_max_chars(void); uint8_t oled_max_lines(void); ``` -!> Scrolling and rotation are unsupported on the SH1106. +!> Scrolling is unsupported on the SH1106 and SH1107. + +!> Scrolling does not work properly on the SSD1306 if the display width is smaller than 128. ## SSD1306.h Driver Conversion Guide diff --git a/drivers/oled/oled_driver.h b/drivers/oled/oled_driver.h index 4d7f2c1d3326..ef69ce29d0cc 100644 --- a/drivers/oled/oled_driver.h +++ b/drivers/oled/oled_driver.h @@ -69,6 +69,115 @@ along with this program. If not, see . // If OLED_BLOCK_TYPE is uint8_t, these tables would look like: // #define OLED_SOURCE_MAP { 0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120 } // #define OLED_TARGET_MAP { 56, 120, 48, 112, 40, 104, 32, 96, 24, 88, 16, 80, 8, 72, 0, 64 } + +#elif defined(OLED_DISPLAY_64X32) +// Double height 128x64 +# ifndef OLED_DISPLAY_WIDTH +# define OLED_DISPLAY_WIDTH 64 +# endif +# ifndef OLED_DISPLAY_HEIGHT +# define OLED_DISPLAY_HEIGHT 32 +# endif +# ifndef OLED_COLUMN_OFFSET +# define OLED_COLUMN_OFFSET 32 +# endif +# ifndef OLED_MATRIX_SIZE +# define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH) +# endif +# ifndef OLED_BLOCK_TYPE +# define OLED_BLOCK_TYPE uint8_t +# endif +# ifndef OLED_BLOCK_COUNT +# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) // 32 (compile time mathed) +# endif +# ifndef OLED_BLOCK_SIZE +# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) // 32 (compile time mathed) +# endif +# ifndef OLED_COM_PINS +# define OLED_COM_PINS COM_PINS_ALT +# endif + +# ifndef OLED_SOURCE_MAP +# define OLED_SOURCE_MAP \ + { 0, 8, 16, 24 } +# endif +# ifndef OLED_TARGET_MAP +# define OLED_TARGET_MAP \ + { 24, 16, 8, 0 } +# endif + +#elif defined(OLED_DISPLAY_64X48) +// Double height 128x64 +# ifndef OLED_DISPLAY_WIDTH +# define OLED_DISPLAY_WIDTH 64 +# endif +# ifndef OLED_DISPLAY_HEIGHT +# define OLED_DISPLAY_HEIGHT 48 +# endif +# ifndef OLED_COLUMN_OFFSET +# define OLED_COLUMN_OFFSET 32 +# endif +# ifndef OLED_MATRIX_SIZE +# define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH) +# endif +# ifndef OLED_BLOCK_TYPE +# define OLED_BLOCK_TYPE uint32_t +# endif +# ifndef OLED_BLOCK_COUNT +# define OLED_BLOCK_COUNT 24 +# endif +# ifndef OLED_BLOCK_SIZE +# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) +# endif +# ifndef OLED_COM_PINS +# define OLED_COM_PINS COM_PINS_ALT +# endif + +# ifndef OLED_SOURCE_MAP +# define OLED_SOURCE_MAP \ + { 0, 8 } +# endif +# ifndef OLED_TARGET_MAP +# define OLED_TARGET_MAP \ + { 8, 0 } +# endif + +#elif defined(OLED_DISPLAY_64X128) +// Double height 128x64 +# ifndef OLED_DISPLAY_WIDTH +# define OLED_DISPLAY_WIDTH 64 +# endif +# ifndef OLED_DISPLAY_HEIGHT +# define OLED_DISPLAY_HEIGHT 128 +# endif +# ifndef OLED_COLUMN_OFFSET +# define OLED_COLUMN_OFFSET 32 +# endif +# ifndef OLED_MATRIX_SIZE +# define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH) +# endif +# ifndef OLED_BLOCK_TYPE +# define OLED_BLOCK_TYPE uint16_t +# endif +# ifndef OLED_BLOCK_COUNT +# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) +# endif +# ifndef OLED_BLOCK_SIZE +# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) +# endif +# ifndef OLED_COM_PINS +# define OLED_COM_PINS COM_PINS_ALT +# endif + +# ifndef OLED_SOURCE_MAP +# define OLED_SOURCE_MAP \ + { 0, 8, 16, 24, 32, 40, 48, 56 } +# endif +# ifndef OLED_TARGET_MAP +# define OLED_TARGET_MAP \ + { 56, 48, 40, 32, 24, 16, 8, 0 } +# endif + #elif defined(OLED_DISPLAY_128X128) // Quad height 128x128 # ifndef OLED_DISPLAY_WIDTH From f094cfb8caf9a2b78db4dbb6a0cbba0761894d2c Mon Sep 17 00:00:00 2001 From: Drashna Jael're Date: Tue, 21 Mar 2023 02:23:24 -0700 Subject: [PATCH 04/26] Remove unneeded files --- .../adafruit/macropad/lib/oled_driver_spi.h | 29 - .../adafruit/macropad/lib/ssd1306_sh1106.c | 825 ------------------ 2 files changed, 854 deletions(-) delete mode 100644 keyboards/adafruit/macropad/lib/oled_driver_spi.h delete mode 100644 keyboards/adafruit/macropad/lib/ssd1306_sh1106.c diff --git a/keyboards/adafruit/macropad/lib/oled_driver_spi.h b/keyboards/adafruit/macropad/lib/oled_driver_spi.h deleted file mode 100644 index c8a2cd7d9a40..000000000000 --- a/keyboards/adafruit/macropad/lib/oled_driver_spi.h +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright 2022 Jose Pablo Ramirez - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once - -#ifndef OLED_DC_PIN -# error "The OLED driver in SPI needs a D/C pin defined" -#endif -#ifndef OLED_CS_PIN -# error "The OLED driver in SPI needs a CS pin defined" -#endif -#ifndef OLED_SPI_MODE -# define OLED_SPI_MODE 3 -#endif -#ifndef OLED_SPI_DIVISOR -# define OLED_SPI_DIVISOR 2 -#endif diff --git a/keyboards/adafruit/macropad/lib/ssd1306_sh1106.c b/keyboards/adafruit/macropad/lib/ssd1306_sh1106.c deleted file mode 100644 index 1b0580964057..000000000000 --- a/keyboards/adafruit/macropad/lib/ssd1306_sh1106.c +++ /dev/null @@ -1,825 +0,0 @@ -/* -Copyright 2019 Ryan Caltabiano -Copyright 2022 Jose Pablo Ramirez -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ -#include "oled_driver.h" -#include "oled_driver_spi.h" - -#include "spi_master.h" - -#include -#include OLED_FONT_H -#include "timer.h" -#include "print.h" - -#include - -#include "progmem.h" - -#include "keyboard.h" - -// Used commands from spec sheet: https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf -// for SH1106: https://www.velleman.eu/downloads/29/infosheets/sh1106_datasheet.pdf - -// Fundamental Commands -#define CONTRAST 0x81 -#define DISPLAY_ALL_ON 0xA5 -#define DISPLAY_ALL_ON_RESUME 0xA4 -#define NORMAL_DISPLAY 0xA6 -#define INVERT_DISPLAY 0xA7 -#define DISPLAY_ON 0xAF -#define DISPLAY_OFF 0xAE -#define NOP 0xE3 - -// Scrolling Commands -#define ACTIVATE_SCROLL 0x2F -#define DEACTIVATE_SCROLL 0x2E -#define SCROLL_RIGHT 0x26 -#define SCROLL_LEFT 0x27 -#define SCROLL_RIGHT_UP 0x29 -#define SCROLL_LEFT_UP 0x2A - -// Addressing Setting Commands -#define MEMORY_MODE 0x20 -#define COLUMN_ADDR 0x21 -#define PAGE_ADDR 0x22 -#define PAM_SETCOLUMN_LSB 0x00 -#define PAM_SETCOLUMN_MSB 0x10 -#define PAM_PAGE_ADDR 0xB0 // 0xb0 -- 0xb7 - -// Hardware Configuration Commands -#define DISPLAY_START_LINE 0x40 -#define SEGMENT_REMAP 0xA0 -#define SEGMENT_REMAP_INV 0xA1 -#define MULTIPLEX_RATIO 0xA8 -#define COM_SCAN_INC 0xC0 -#define COM_SCAN_DEC 0xC8 -#define DISPLAY_OFFSET 0xD3 -#define COM_PINS 0xDA -#define COM_PINS_SEQ 0x02 -#define COM_PINS_ALT 0x12 -#define COM_PINS_SEQ_LR 0x22 -#define COM_PINS_ALT_LR 0x32 - -// Timing & Driving Commands -#define DISPLAY_CLOCK 0xD5 -#define PRE_CHARGE_PERIOD 0xD9 -#define VCOM_DETECT 0xDB - -// Advance Graphic Commands -#define FADE_BLINK 0x23 -#define ENABLE_FADE 0x20 -#define ENABLE_BLINK 0x30 - -// Charge Pump Commands -#define CHARGE_PUMP 0x8D - -// Misc defines -#ifndef OLED_BLOCK_COUNT -# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) -#endif -#ifndef OLED_BLOCK_SIZE -# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) -#endif - -#define OLED_ALL_BLOCKS_MASK (((((OLED_BLOCK_TYPE)1 << (OLED_BLOCK_COUNT - 1)) - 1) << 1) | 1) - -// spi defines -#define OLED_STATUS_SUCCESS SPI_STATUS_SUCCESS - -void oled_spi_init(void) { - spi_init(); - - setPinOutput(OLED_CS_PIN); - writePinHigh(OLED_CS_PIN); - - setPinOutput(OLED_DC_PIN); - writePinLow(OLED_DC_PIN); -} - -void oled_spi_start(void) { - spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR); -} - -void oled_spi_stop(void) { - spi_stop(); -} - -// Transmit/Write Funcs. -bool oled_cmd(const uint8_t *data, uint16_t size) { - oled_spi_start(); - // Command Mode - writePinLow(OLED_DC_PIN); - // Send the commands - if(spi_transmit(data, size) != OLED_STATUS_SUCCESS){ - oled_spi_stop(); - return false; - } - oled_spi_stop(); - return true; -} - -bool oled_cmd_p(const uint8_t *data, uint16_t size) { - return oled_cmd(data, size); -} - -bool oled_write_reg(const uint8_t *data, uint16_t size) -{ - oled_spi_start(); - // Command Mode - writePinHigh(OLED_DC_PIN); - // Send the commands - if(spi_transmit(data, size) != OLED_STATUS_SUCCESS){ - oled_spi_stop(); - return false; - } - oled_spi_stop(); - return true; -} - -#define HAS_FLAGS(bits, flags) ((bits & flags) == flags) - -// Display buffer's is the same as the OLED memory layout -// this is so we don't end up with rounding errors with -// parts of the display unusable or don't get cleared correctly -// and also allows for drawing & inverting -uint8_t oled_buffer[OLED_MATRIX_SIZE]; -uint8_t * oled_cursor; -OLED_BLOCK_TYPE oled_dirty = 0; -bool oled_initialized = false; -bool oled_active = false; -bool oled_scrolling = false; -bool oled_inverted = false; -uint8_t oled_brightness = OLED_BRIGHTNESS; -oled_rotation_t oled_rotation = 0; -uint8_t oled_rotation_width = 0; -uint8_t oled_scroll_speed = 0; // this holds the speed after being remapped to ssd1306 internal values -uint8_t oled_scroll_start = 0; -uint8_t oled_scroll_end = 7; -#if OLED_TIMEOUT > 0 -uint32_t oled_timeout; -#endif -#if OLED_SCROLL_TIMEOUT > 0 -uint32_t oled_scroll_timeout; -#endif -#if OLED_UPDATE_INTERVAL > 0 -uint16_t oled_update_timeout; -#endif - -// Flips the rendering bits for a character at the current cursor position -static void InvertCharacter(uint8_t *cursor) { - const uint8_t *end = cursor + OLED_FONT_WIDTH; - while (cursor < end) { - *cursor = ~(*cursor); - cursor++; - } -} - -bool oled_init(oled_rotation_t rotation) { - oled_rotation = oled_init_user(oled_init_kb(rotation)); - if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { - oled_rotation_width = OLED_DISPLAY_WIDTH; - } else { - oled_rotation_width = OLED_DISPLAY_HEIGHT; - } - - oled_spi_init(); - -#ifdef OLED_RST_PIN - /* Reset device */ - setPinOutput(OLED_RST_PIN); - writePinLow(OLED_RST_PIN); - wait_ms(20); - writePinHigh(OLED_RST_PIN); - wait_ms(20); -#endif - - static const uint8_t PROGMEM display_setup1[] = { - DISPLAY_OFF, - DISPLAY_CLOCK, - 0x80, - MULTIPLEX_RATIO, - OLED_DISPLAY_HEIGHT - 1, - DISPLAY_OFFSET, - 0x00, - DISPLAY_START_LINE | 0x00, - CHARGE_PUMP, - 0x14, -#if (OLED_IC != OLED_IC_SH1106) - // MEMORY_MODE is unsupported on SH1106 (Page Addressing only) - MEMORY_MODE, - 0x00, // Horizontal addressing mode -#endif - }; - - if (!oled_cmd_p(display_setup1, ARRAY_SIZE(display_setup1))) { - print("oled_init cmd set 1 failed\n"); - return false; - } - - if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_180)) { - static const uint8_t PROGMEM display_normal[] = {SEGMENT_REMAP_INV, COM_SCAN_DEC}; - if (!oled_cmd_p(display_normal, ARRAY_SIZE(display_normal))) { - print("oled_init cmd normal rotation failed\n"); - return false; - } - } else { - static const uint8_t PROGMEM display_flipped[] = {SEGMENT_REMAP, COM_SCAN_INC}; - if (!oled_cmd_p(display_flipped, ARRAY_SIZE(display_flipped))) { - print("display_flipped failed\n"); - return false; - } - } - - static const uint8_t PROGMEM display_setup2[] = {COM_PINS, OLED_COM_PINS, CONTRAST, OLED_BRIGHTNESS, PRE_CHARGE_PERIOD, 0xF1, VCOM_DETECT, 0x20, DISPLAY_ALL_ON_RESUME, NORMAL_DISPLAY, DEACTIVATE_SCROLL, DISPLAY_ON}; - if (!oled_cmd_p(display_setup2, ARRAY_SIZE(display_setup2))) { - print("display_setup2 failed\n"); - return false; - } - -#if OLED_TIMEOUT > 0 - oled_timeout = timer_read32() + OLED_TIMEOUT; -#endif -#if OLED_SCROLL_TIMEOUT > 0 - oled_scroll_timeout = timer_read32() + OLED_SCROLL_TIMEOUT; -#endif - - oled_clear(); - oled_initialized = true; - oled_active = true; - oled_scrolling = false; - return true; -} - -__attribute__((weak)) oled_rotation_t oled_init_kb(oled_rotation_t rotation) { - return rotation; -} -__attribute__((weak)) oled_rotation_t oled_init_user(oled_rotation_t rotation) { - return rotation; -} - -void oled_clear(void) { - memset(oled_buffer, 0, sizeof(oled_buffer)); - oled_cursor = &oled_buffer[0]; - oled_dirty = OLED_ALL_BLOCKS_MASK; -} - -static void calc_bounds(uint8_t update_start, uint8_t *cmd_array) { - // Calculate commands to set memory addressing bounds. - uint8_t start_page = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_WIDTH; - uint8_t start_column = OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_WIDTH; -#if (OLED_IC == OLED_IC_SH1106) - // Commands for Page Addressing Mode. Sets starting page and column; has no end bound. - // Column value must be split into high and low nybble and sent as two commands. - cmd_array[0] = PAM_PAGE_ADDR | start_page; - cmd_array[1] = PAM_SETCOLUMN_LSB | ((OLED_COLUMN_OFFSET + start_column) & 0x0f); - cmd_array[2] = PAM_SETCOLUMN_MSB | ((OLED_COLUMN_OFFSET + start_column) >> 4 & 0x0f); - cmd_array[3] = NOP; - cmd_array[4] = NOP; - cmd_array[5] = NOP; -#else - // Commands for use in Horizontal Addressing mode. - cmd_array[1] = start_column; - cmd_array[4] = start_page; - cmd_array[2] = (OLED_BLOCK_SIZE + OLED_DISPLAY_WIDTH - 1) % OLED_DISPLAY_WIDTH + cmd_array[1]; - cmd_array[5] = (OLED_BLOCK_SIZE + OLED_DISPLAY_WIDTH - 1) / OLED_DISPLAY_WIDTH - 1; -#endif -} - -static void calc_bounds_90(uint8_t update_start, uint8_t *cmd_array) { - cmd_array[1] = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_HEIGHT * 8; - cmd_array[4] = OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_HEIGHT; - cmd_array[2] = (OLED_BLOCK_SIZE + OLED_DISPLAY_HEIGHT - 1) / OLED_DISPLAY_HEIGHT * 8 - 1 + cmd_array[1]; - ; - cmd_array[5] = (OLED_BLOCK_SIZE + OLED_DISPLAY_HEIGHT - 1) % OLED_DISPLAY_HEIGHT / 8; -} - -uint8_t crot(uint8_t a, int8_t n) { - const uint8_t mask = 0x7; - n &= mask; - return a << n | a >> (-n & mask); -} - -static void rotate_90(const uint8_t *src, uint8_t *dest) { - for (uint8_t i = 0, shift = 7; i < 8; ++i, --shift) { - uint8_t selector = (1 << i); - for (uint8_t j = 0; j < 8; ++j) { - dest[i] |= crot(src[j] & selector, shift - (int8_t)j); - } - } -} - -void oled_render(void) { - if (!oled_initialized) { - return; - } - - // Do we have work to do? - oled_dirty &= OLED_ALL_BLOCKS_MASK; - if (!oled_dirty || oled_scrolling) { - return; - } - - // Find first dirty block - uint8_t update_start = 0; - while (!(oled_dirty & ((OLED_BLOCK_TYPE)1 << update_start))) { - ++update_start; - } - - // Set column & page position - static uint8_t display_start[] = {COLUMN_ADDR, 0, OLED_DISPLAY_WIDTH - 1, PAGE_ADDR, 0, OLED_DISPLAY_HEIGHT / 8 - 1}; - if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { - calc_bounds(update_start, display_start); - } else { - calc_bounds_90(update_start, display_start); - } - - // Send column & page position - if (!oled_cmd(display_start, ARRAY_SIZE(display_start))) { - print("oled_render offset command failed\n"); - return; - } - - if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { - // Send render data chunk as is - if (!oled_write_reg(&oled_buffer[OLED_BLOCK_SIZE * update_start], OLED_BLOCK_SIZE)) { - print("oled_render data failed\n"); - return; - } - } else { - // Rotate the render chunks - const static uint8_t source_map[] = OLED_SOURCE_MAP; - const static uint8_t target_map[] = OLED_TARGET_MAP; - - static uint8_t temp_buffer[OLED_BLOCK_SIZE]; - memset(temp_buffer, 0, sizeof(temp_buffer)); - for (uint8_t i = 0; i < sizeof(source_map); ++i) { - rotate_90(&oled_buffer[OLED_BLOCK_SIZE * update_start + source_map[i]], &temp_buffer[target_map[i]]); - } - - // Send render data chunk after rotating - if (!oled_write_reg(temp_buffer, OLED_BLOCK_SIZE)) { - print("oled_render90 data failed\n"); - return; - } - } - - // Turn on display if it is off - oled_on(); - - // Clear dirty flag - oled_dirty &= ~((OLED_BLOCK_TYPE)1 << update_start); -} - -void oled_set_cursor(uint8_t col, uint8_t line) { - uint16_t index = line * oled_rotation_width + col * OLED_FONT_WIDTH; - - // Out of bounds? - if (index >= OLED_MATRIX_SIZE) { - index = 0; - } - - oled_cursor = &oled_buffer[index]; -} - -void oled_advance_page(bool clearPageRemainder) { - uint16_t index = oled_cursor - &oled_buffer[0]; - uint8_t remaining = oled_rotation_width - (index % oled_rotation_width); - - if (clearPageRemainder) { - // Remaining Char count - remaining = remaining / OLED_FONT_WIDTH; - - // Write empty character until next line - while (remaining--) - oled_write_char(' ', false); - } else { - // Next page index out of bounds? - if (index + remaining >= OLED_MATRIX_SIZE) { - index = 0; - remaining = 0; - } - - oled_cursor = &oled_buffer[index + remaining]; - } -} - -void oled_advance_char(void) { - uint16_t nextIndex = oled_cursor - &oled_buffer[0] + OLED_FONT_WIDTH; - uint8_t remainingSpace = oled_rotation_width - (nextIndex % oled_rotation_width); - - // Do we have enough space on the current line for the next character - if (remainingSpace < OLED_FONT_WIDTH) { - nextIndex += remainingSpace; - } - - // Did we go out of bounds - if (nextIndex >= OLED_MATRIX_SIZE) { - nextIndex = 0; - } - - // Update cursor position - oled_cursor = &oled_buffer[nextIndex]; -} - -// Main handler that writes character data to the display buffer -void oled_write_char(const char data, bool invert) { - // Advance to the next line if newline - if (data == '\n') { - // Old source wrote ' ' until end of line... - oled_advance_page(true); - return; - } - - if (data == '\r') { - oled_advance_page(false); - return; - } - - // copy the current render buffer to check for dirty after - static uint8_t oled_temp_buffer[OLED_FONT_WIDTH]; - memcpy(&oled_temp_buffer, oled_cursor, OLED_FONT_WIDTH); - - _Static_assert(sizeof(font) >= ((OLED_FONT_END + 1 - OLED_FONT_START) * OLED_FONT_WIDTH), "OLED_FONT_END references outside array"); - - // set the reder buffer data - uint8_t cast_data = (uint8_t)data; // font based on unsigned type for index - if (cast_data < OLED_FONT_START || cast_data > OLED_FONT_END) { - memset(oled_cursor, 0x00, OLED_FONT_WIDTH); - } else { - const uint8_t *glyph = &font[(cast_data - OLED_FONT_START) * OLED_FONT_WIDTH]; - memcpy_P(oled_cursor, glyph, OLED_FONT_WIDTH); - } - - // Invert if needed - if (invert) { - InvertCharacter(oled_cursor); - } - - // Dirty check - if (memcmp(&oled_temp_buffer, oled_cursor, OLED_FONT_WIDTH)) { - uint16_t index = oled_cursor - &oled_buffer[0]; - oled_dirty |= ((OLED_BLOCK_TYPE)1 << (index / OLED_BLOCK_SIZE)); - // Edgecase check if the written data spans the 2 chunks - oled_dirty |= ((OLED_BLOCK_TYPE)1 << ((index + OLED_FONT_WIDTH - 1) / OLED_BLOCK_SIZE)); - } - - // Finally move to the next char - oled_advance_char(); -} - -void oled_write(const char *data, bool invert) { - const char *end = data + strlen(data); - while (data < end) { - oled_write_char(*data, invert); - data++; - } -} - -void oled_write_ln(const char *data, bool invert) { - oled_write(data, invert); - oled_advance_page(true); -} - -void oled_pan(bool left) { - uint16_t i = 0; - for (uint16_t y = 0; y < OLED_DISPLAY_HEIGHT / 8; y++) { - if (left) { - for (uint16_t x = 0; x < OLED_DISPLAY_WIDTH - 1; x++) { - i = y * OLED_DISPLAY_WIDTH + x; - oled_buffer[i] = oled_buffer[i + 1]; - } - } else { - for (uint16_t x = OLED_DISPLAY_WIDTH - 1; x > 0; x--) { - i = y * OLED_DISPLAY_WIDTH + x; - oled_buffer[i] = oled_buffer[i - 1]; - } - } - } - oled_dirty = OLED_ALL_BLOCKS_MASK; -} - -oled_buffer_reader_t oled_read_raw(uint16_t start_index) { - if (start_index > OLED_MATRIX_SIZE) start_index = OLED_MATRIX_SIZE; - oled_buffer_reader_t ret_reader; - ret_reader.current_element = &oled_buffer[start_index]; - ret_reader.remaining_element_count = OLED_MATRIX_SIZE - start_index; - return ret_reader; -} - -void oled_write_raw_byte(const char data, uint16_t index) { - if (index > OLED_MATRIX_SIZE) index = OLED_MATRIX_SIZE; - if (oled_buffer[index] == data) return; - oled_buffer[index] = data; - oled_dirty |= ((OLED_BLOCK_TYPE)1 << (index / OLED_BLOCK_SIZE)); -} - -void oled_write_raw(const char *data, uint16_t size) { - uint16_t cursor_start_index = oled_cursor - &oled_buffer[0]; - if ((size + cursor_start_index) > OLED_MATRIX_SIZE) size = OLED_MATRIX_SIZE - cursor_start_index; - for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) { - uint8_t c = *data++; - if (oled_buffer[i] == c) continue; - oled_buffer[i] = c; - oled_dirty |= ((OLED_BLOCK_TYPE)1 << (i / OLED_BLOCK_SIZE)); - } -} - -void oled_write_pixel(uint8_t x, uint8_t y, bool on) { - if (x >= oled_rotation_width) { - return; - } - uint16_t index = x + (y / 8) * oled_rotation_width; - if (index >= OLED_MATRIX_SIZE) { - return; - } - uint8_t data = oled_buffer[index]; - if (on) { - data |= (1 << (y % 8)); - } else { - data &= ~(1 << (y % 8)); - } - if (oled_buffer[index] != data) { - oled_buffer[index] = data; - oled_dirty |= ((OLED_BLOCK_TYPE)1 << (index / OLED_BLOCK_SIZE)); - } -} - -#if defined(__AVR__) -void oled_write_P(const char *data, bool invert) { - uint8_t c = pgm_read_byte(data); - while (c != 0) { - oled_write_char(c, invert); - c = pgm_read_byte(++data); - } -} - -void oled_write_ln_P(const char *data, bool invert) { - oled_write_P(data, invert); - oled_advance_page(true); -} - -void oled_write_raw_P(const char *data, uint16_t size) { - uint16_t cursor_start_index = oled_cursor - &oled_buffer[0]; - if ((size + cursor_start_index) > OLED_MATRIX_SIZE) size = OLED_MATRIX_SIZE - cursor_start_index; - for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) { - uint8_t c = pgm_read_byte(data++); - if (oled_buffer[i] == c) continue; - oled_buffer[i] = c; - oled_dirty |= ((OLED_BLOCK_TYPE)1 << (i / OLED_BLOCK_SIZE)); - } -} -#endif // defined(__AVR__) - -bool oled_on(void) { - if (!oled_initialized) { - return oled_active; - } - -#if OLED_TIMEOUT > 0 - oled_timeout = timer_read32() + OLED_TIMEOUT; -#endif - - static const uint8_t PROGMEM display_on[] = -#ifdef OLED_FADE_OUT - {FADE_BLINK, 0x00}; -#else - {DISPLAY_ON}; -#endif - - if (!oled_active) { - if (!oled_cmd_p(display_on, ARRAY_SIZE(display_on))) { - print("oled_on cmd failed\n"); - return oled_active; - } - oled_active = true; - } - return oled_active; -} - -bool oled_off(void) { - if (!oled_initialized) { - return !oled_active; - } - - static const uint8_t PROGMEM display_off[] = -#ifdef OLED_FADE_OUT - {FADE_BLINK, ENABLE_FADE | OLED_FADE_OUT_INTERVAL}; -#else - {DISPLAY_OFF}; -#endif - - if (oled_active) { - if (!oled_cmd_p(display_off, ARRAY_SIZE(display_off))) { - print("oled_off cmd failed\n"); - return oled_active; - } - oled_active = false; - } - return !oled_active; -} - -bool is_oled_on(void) { - return oled_active; -} - -uint8_t oled_set_brightness(uint8_t level) { - if (!oled_initialized) { - return oled_brightness; - } - - uint8_t set_contrast[] = { CONTRAST, level}; - if (oled_brightness != level) { - if (!oled_cmd(set_contrast, ARRAY_SIZE(set_contrast))) { - print("set_brightness cmd failed\n"); - return oled_brightness; - } - oled_brightness = level; - } - return oled_brightness; -} - -uint8_t oled_get_brightness(void) { - return oled_brightness; -} - -// Set the specific 8 lines rows of the screen to scroll. -// 0 is the default for start, and 7 for end, which is the entire -// height of the screen. For 128x32 screens, rows 4-7 are not used. -void oled_scroll_set_area(uint8_t start_line, uint8_t end_line) { - oled_scroll_start = start_line; - oled_scroll_end = end_line; -} - -void oled_scroll_set_speed(uint8_t speed) { - // Sets the speed for scrolling... does not take effect - // until scrolling is either started or restarted - // the ssd1306 supports 8 speeds - // FrameRate2 speed = 7 - // FrameRate3 speed = 4 - // FrameRate4 speed = 5 - // FrameRate5 speed = 0 - // FrameRate25 speed = 6 - // FrameRate64 speed = 1 - // FrameRate128 speed = 2 - // FrameRate256 speed = 3 - // for ease of use these are remaped here to be in order - static const uint8_t scroll_remap[8] = {7, 4, 5, 0, 6, 1, 2, 3}; - oled_scroll_speed = scroll_remap[speed]; -} - -bool oled_scroll_right(void) { - if (!oled_initialized) { - return oled_scrolling; - } - - // Dont enable scrolling if we need to update the display - // This prevents scrolling of bad data from starting the scroll too early after init - if (!oled_dirty && !oled_scrolling) { - uint8_t display_scroll_right[] = {SCROLL_RIGHT, 0x00, oled_scroll_start, oled_scroll_speed, oled_scroll_end, 0x00, 0xFF, ACTIVATE_SCROLL}; - if (!oled_cmd(display_scroll_right, ARRAY_SIZE(display_scroll_right))) { - print("oled_scroll_right cmd failed\n"); - return oled_scrolling; - } - oled_scrolling = true; - } - return oled_scrolling; -} - -bool oled_scroll_left(void) { - if (!oled_initialized) { - return oled_scrolling; - } - - // Dont enable scrolling if we need to update the display - // This prevents scrolling of bad data from starting the scroll too early after init - if (!oled_dirty && !oled_scrolling) { - uint8_t display_scroll_left[] = {SCROLL_LEFT, 0x00, oled_scroll_start, oled_scroll_speed, oled_scroll_end, 0x00, 0xFF, ACTIVATE_SCROLL}; - if (!oled_cmd(display_scroll_left, ARRAY_SIZE(display_scroll_left))) { - print("oled_scroll_left cmd failed\n"); - return oled_scrolling; - } - oled_scrolling = true; - } - return oled_scrolling; -} - -bool oled_scroll_off(void) { - if (!oled_initialized) { - return !oled_scrolling; - } - - if (oled_scrolling) { - static const uint8_t PROGMEM display_scroll_off[] = {DEACTIVATE_SCROLL}; - if (!oled_cmd_p(display_scroll_off, ARRAY_SIZE(display_scroll_off))) { - print("oled_scroll_off cmd failed\n"); - return oled_scrolling; - } - oled_scrolling = false; - oled_dirty = OLED_ALL_BLOCKS_MASK; - } - return !oled_scrolling; -} - -bool is_oled_scrolling(void) { - return oled_scrolling; -} - -bool oled_invert(bool invert) { - if (!oled_initialized) { - return oled_inverted; - } - - if (invert && !oled_inverted) { - static const uint8_t PROGMEM display_inverted[] = {INVERT_DISPLAY}; - if (!oled_cmd_p(display_inverted, ARRAY_SIZE(display_inverted))) { - print("oled_invert cmd failed\n"); - return oled_inverted; - } - oled_inverted = true; - } else if (!invert && oled_inverted) { - static const uint8_t PROGMEM display_normal[] = {NORMAL_DISPLAY}; - if (!oled_cmd_p(display_normal, ARRAY_SIZE(display_normal))) { - print("oled_invert cmd failed\n"); - return oled_inverted; - } - oled_inverted = false; - } - - return oled_inverted; -} - -uint8_t oled_max_chars(void) { - if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { - return OLED_DISPLAY_WIDTH / OLED_FONT_WIDTH; - } - return OLED_DISPLAY_HEIGHT / OLED_FONT_WIDTH; -} - -uint8_t oled_max_lines(void) { - if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { - return OLED_DISPLAY_HEIGHT / OLED_FONT_HEIGHT; - } - return OLED_DISPLAY_WIDTH / OLED_FONT_HEIGHT; -} - -void oled_task(void) { - if (!oled_initialized) { - return; - } - -#if OLED_UPDATE_INTERVAL > 0 - if (timer_elapsed(oled_update_timeout) >= OLED_UPDATE_INTERVAL) { - oled_update_timeout = timer_read(); - oled_set_cursor(0, 0); - oled_task_kb(); - } -#else - oled_set_cursor(0, 0); - oled_task_kb(); -#endif - -#if OLED_SCROLL_TIMEOUT > 0 - if (oled_dirty && oled_scrolling) { - oled_scroll_timeout = timer_read32() + OLED_SCROLL_TIMEOUT; - oled_scroll_off(); - } -#endif - - // Smart render system, no need to check for dirty - oled_render(); - - // Display timeout check -#if OLED_TIMEOUT > 0 - if (oled_active && timer_expired32(timer_read32(), oled_timeout)) { - oled_off(); - } -#endif - -#if OLED_SCROLL_TIMEOUT > 0 - if (!oled_scrolling && timer_expired32(timer_read32(), oled_scroll_timeout)) { -# ifdef OLED_SCROLL_TIMEOUT_RIGHT - oled_scroll_right(); -# else - oled_scroll_left(); -# endif - } -#endif -} - -__attribute__((weak)) bool oled_task_kb(void) { - return oled_task_user(); -} -__attribute__((weak)) bool oled_task_user(void) { - return true; -} From 0e83c49a3acfaf2bf01ed0aef3141d4fa10a8d40 Mon Sep 17 00:00:00 2001 From: Drashna Jael're Date: Tue, 21 Mar 2023 02:41:14 -0700 Subject: [PATCH 05/26] Fix compilation errors and formatting --- drivers/oled/oled_driver.c | 51 ++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/drivers/oled/oled_driver.c b/drivers/oled/oled_driver.c index f1fd65e1ec9c..da1700909688 100644 --- a/drivers/oled/oled_driver.c +++ b/drivers/oled/oled_driver.c @@ -15,20 +15,24 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +#ifdef OLED_DRIVER_SPI +# include "spi_master.h" +#else +# include "i2c_master.h" +#endif #include "oled_driver.h" -#include "oled_common.h" #include OLED_FONT_H #include "timer.h" #include "print.h" #include #include "progmem.h" #include "keyboard.h" +#include "wait.h" // Used commands from spec sheet: https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf // for SH1106: https://www.velleman.eu/downloads/29/infosheets/sh1106_datasheet.pdf // for SH1107: https://www.displayfuture.com/Display/datasheet/controller/SH1107.pdf - #include "progmem.h" // Fundamental Commands @@ -123,7 +127,7 @@ along with this program. If not, see . #define I2C_DATA 0x40 #undef ARRAY_SIZE -#define ARRAY_SIZE(arr) sizeof(arr)/sizeof(arr[0]) +#define ARRAY_SIZE(arr) sizeof(arr) / sizeof(arr[0]) #define HAS_FLAGS(bits, flags) ((bits & flags) == flags) @@ -132,7 +136,7 @@ along with this program. If not, see . // parts of the display unusable or don't get cleared correctly // and also allows for drawing & inverting uint8_t oled_buffer[OLED_MATRIX_SIZE]; -uint8_t * oled_cursor; +uint8_t *oled_cursor; OLED_BLOCK_TYPE oled_dirty = 0; bool oled_initialized = false; bool oled_active = false; @@ -173,7 +177,6 @@ uint16_t oled_update_timeout; # endif #endif - // Transmit/Write Funcs. bool oled_cmd(const uint8_t *data, uint16_t size) { #ifdef OLED_DRIVER_SPI @@ -194,7 +197,7 @@ bool oled_cmd(const uint8_t *data, uint16_t size) { #endif } -oled_cmd_P(const uint8_t *data, uint16_t size) { +bool oled_cmd_P(const uint8_t *data, uint16_t size) { #if defined(__AVR__) && !defined(OLED_DRIVER_SPI) i2c_status_t status = i2c_start((OLED_DISPLAY_ADDRESS << 1) | I2C_WRITE, OLED_I2C_TIMEOUT); @@ -222,9 +225,8 @@ bool oled_write_reg(const uint8_t *data, uint16_t size) { return false; } spi_stop(); -#else return true; - +#else i2c_status_t status = i2c_writeReg((OLED_DISPLAY_ADDRESS << 1), I2C_DATA, data, size, OLED_I2C_TIMEOUT); return (status == I2C_STATUS_SUCCESS); #endif @@ -275,7 +277,6 @@ bool oled_init(oled_rotation_t rotation) { } oled_driver_init(); - static const uint8_t PROGMEM display_setup1[] = { I2C_CMD, DISPLAY_OFF, @@ -288,8 +289,8 @@ bool oled_init(oled_rotation_t rotation) { OLED_DISPLAY_HEIGHT - 1, #endif #if OLED_IC == OLED_IC_SH1107 - SH1107_DISPLAY_START_LINE, - 0x00, + SH1107_DISPLAY_START_LINE, + 0x00, #else DISPLAY_START_LINE | 0x00, #endif @@ -298,10 +299,10 @@ bool oled_init(oled_rotation_t rotation) { #if OLED_IC_HAS_HORIZONTAL_MODE // MEMORY_MODE is unsupported on SH1106 (Page Addressing only) MEMORY_MODE, - 0x00, // Horizontal addressing mode + 0x00, // Horizontal addressing mode #elif OLED_IC == OLED_IC_SH1107 - // Page addressing mode - SH1107_MEMORY_MODE_PAGE, + // Page addressing mode + SH1107_MEMORY_MODE_PAGE, #endif }; if (!oled_cmd_P(display_setup1, ARRAY_SIZE(display_setup1))) { @@ -311,11 +312,7 @@ bool oled_init(oled_rotation_t rotation) { if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_180)) { static const uint8_t PROGMEM display_normal[] = { - I2C_CMD, - SEGMENT_REMAP_INV, - COM_SCAN_DEC, - DISPLAY_OFFSET, - OLED_COM_PIN_OFFSET, + I2C_CMD, SEGMENT_REMAP_INV, COM_SCAN_DEC, DISPLAY_OFFSET, OLED_COM_PIN_OFFSET, }; if (!oled_cmd_P(display_normal, ARRAY_SIZE(display_normal))) { print("oled_init cmd normal rotation failed\n"); @@ -323,11 +320,7 @@ bool oled_init(oled_rotation_t rotation) { } } else { static const uint8_t PROGMEM display_flipped[] = { - I2C_CMD, - SEGMENT_REMAP, - COM_SCAN_INC, - DISPLAY_OFFSET, - (OLED_COM_PIN_COUNT - OLED_COM_PIN_OFFSET) % OLED_COM_PIN_COUNT, + I2C_CMD, SEGMENT_REMAP, COM_SCAN_INC, DISPLAY_OFFSET, (OLED_COM_PIN_COUNT - OLED_COM_PIN_OFFSET) % OLED_COM_PIN_COUNT, }; if (!oled_cmd_P(display_flipped, ARRAY_SIZE(display_flipped))) { print("display_flipped failed\n"); @@ -406,9 +399,9 @@ static void calc_bounds_90(uint8_t update_start, uint8_t *cmd_array) { // Only the Page Addressing Mode is supported uint8_t start_page = bottom_block_top_page - (OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_HEIGHT / 8); uint8_t start_column = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_HEIGHT * 8; - cmd_array[0] = PAM_PAGE_ADDR | start_page; - cmd_array[1] = PAM_SETCOLUMN_LSB | ((OLED_COLUMN_OFFSET + start_column) & 0x0f); - cmd_array[2] = PAM_SETCOLUMN_MSB | ((OLED_COLUMN_OFFSET + start_column) >> 4 & 0x0f); + cmd_array[0] = PAM_PAGE_ADDR | start_page; + cmd_array[1] = PAM_SETCOLUMN_LSB | ((OLED_COLUMN_OFFSET + start_column) & 0x0f); + cmd_array[2] = PAM_SETCOLUMN_MSB | ((OLED_COLUMN_OFFSET + start_column) >> 4 & 0x0f); #else cmd_array[1] = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_HEIGHT * 8 + OLED_COLUMN_OFFSET; cmd_array[4] = bottom_block_top_page - (OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_HEIGHT / 8); @@ -494,7 +487,7 @@ void oled_render(void) { #else // For SH1106 or SH1107 the data chunk must be split into separate pieces for each page const uint8_t columns_in_block = (OLED_BLOCK_SIZE + OLED_DISPLAY_HEIGHT - 1) / OLED_DISPLAY_HEIGHT * 8; - const uint8_t num_pages = OLED_BLOCK_SIZE / columns_in_block; + const uint8_t num_pages = OLED_BLOCK_SIZE / columns_in_block; for (uint8_t i = 0; i < num_pages; ++i) { // Send column & page position for all pages except the first one if (i > 0) { @@ -665,8 +658,6 @@ void oled_pan_section(bool left, uint16_t y_start, uint16_t y_end, uint16_t x_st } } - - oled_buffer_reader_t oled_read_raw(uint16_t start_index) { if (start_index > OLED_MATRIX_SIZE) start_index = OLED_MATRIX_SIZE; oled_buffer_reader_t ret_reader; From 4fc45a2bb0c0a6282e8966c2b0b905360a4fcfb7 Mon Sep 17 00:00:00 2001 From: Drashna Jael're Date: Mon, 3 Apr 2023 16:05:42 -0700 Subject: [PATCH 06/26] Revert driver make logic and move spi/i2c to transport setting --- builddefs/common_features.mk | 35 ++++++++++++---------- docs/feature_oled_driver.md | 27 ++++++++++++----- drivers/oled/oled_driver.c | 45 ++++++++++++++++++---------- keyboards/adafruit/macropad/rules.mk | 2 +- 4 files changed, 68 insertions(+), 41 deletions(-) diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk index 0be221a30aa0..92c6bc47d5d2 100644 --- a/builddefs/common_features.mk +++ b/builddefs/common_features.mk @@ -735,27 +735,30 @@ ifeq ($(strip $(HD44780_ENABLE)), yes) SRC += hd44780.c endif -VALID_OLED_DRIVER_TYPES := I2C SPI SSD1306 custom -OLED_DRIVER ?= I2C +VALID_OLED_DRIVER_TYPES := SSD1306 custom +OLED_DRIVER ?= SSD1306 +VALID_OLED_TRANSPORT_TYPES := i2c spi custom +OLED_TRANSPORT ?= i2c ifeq ($(strip $(OLED_ENABLE)), yes) ifeq ($(filter $(OLED_DRIVER),$(VALID_OLED_DRIVER_TYPES)),) $(call CATASTROPHIC_ERROR,Invalid OLED_DRIVER,OLED_DRIVER="$(OLED_DRIVER)" is not a valid OLED driver) else - OPT_DEFS += -DOLED_ENABLE - COMMON_VPATH += $(DRIVER_PATH)/oled - ifneq ($(strip $(OLED_DRIVER)), custom) - SRC += oled_driver.c - endif - ifeq ($(strip $(OLED_DRIVER)), SSD1306) - OLED_DRIVER = I2C - endif + ifeq ($(filter $(OLED_TRANSPORT),$(VALID_OLED_TRANSPORT_TYPES)),) + $(call CATASTROPHIC_ERROR,Invalid OLED_TRANSPORT,OLED_TRANSPORT="$(OLED_TRANSPORT)" is not a valid OLED transport) + else + OPT_DEFS += -DOLED_ENABLE + COMMON_VPATH += $(DRIVER_PATH)/oled + ifneq ($(strip $(OLED_DRIVER)), custom) + SRC += oled_driver.c + endif - OPT_DEFS += -DOLED_DRIVER_$(strip $(shell echo $(OLED_DRIVER) | tr '[:lower:]' '[:upper:]')) - ifeq ($(strip $(OLED_DRIVER)), I2C) - QUANTUM_LIB_SRC += i2c_master.c - endif - ifeq ($(strip $(OLED_DRIVER)), SPI) - QUANTUM_LIB_SRC += spi_master.c + OPT_DEFS += -DOLED_TRANSPORT_$(strip $(shell echo $(OLED_TRANSPORT) | tr '[:lower:]' '[:upper:]')) + ifeq ($(strip $(OLED_TRANSPORT)), i2c) + QUANTUM_LIB_SRC += i2c_master.c + endif + ifeq ($(strip $(OLED_TRANSPORT)), spi) + QUANTUM_LIB_SRC += spi_master.c + endif endif endif endif diff --git a/docs/feature_oled_driver.md b/docs/feature_oled_driver.md index 96a887e6f6e7..429966771738 100644 --- a/docs/feature_oled_driver.md +++ b/docs/feature_oled_driver.md @@ -26,14 +26,24 @@ OLED_ENABLE = yes ``` ## OLED type -|OLED Driver | -|-------------------| -|I2C (default) | -|SPI | + +|OLED Driver |Supported Device | +|-------------------|------------------------------------| +|SSD1306 (default) |For both SSD1306, SH1106, and SH1107| e.g. ```make -OLED_DRIVER = I2C +OLED_DRIVER = SSD1306 +``` + +|OLED Transport | | +|---------------|------------------------------------------------| +|i2c (default) | Uses I2C for communication with the OLED panel | +|spi | Uses SPI for communication with the OLED panel | + +e.g. +```make +OLED_TRANSPORT = i2c ``` Then in your `keymap.c` file, implement the OLED task call. This example assumes your keymap has three layers named `_QWERTY`, `_FN` and `_ADJ`: @@ -180,10 +190,13 @@ These configuration options should be placed in `config.h`. Example: |`OLED_BRIGHTNESS` |`255` |The default brightness level of the OLED, from 0 to 255. | |`OLED_UPDATE_INTERVAL` |`0` |Set the time interval for updating the OLED display in ms. This will improve the matrix scan rate. | +### I2C Configuration |Define |Default |Description | |---------------------------|-----------------|--------------------------------------------------------------------------------------------------------------------------| |`OLED_DISPLAY_ADDRESS` |`0x3C` |The i2c address of the OLED Display | +### SPI Configuration + |Define |Default |Description | |---------------------------|-----------------|--------------------------------------------------------------------------------------------------------------------------| |`OLED_DC_PIN` | Required |The pin used for the DC connection of the OLED Display. | @@ -191,8 +204,7 @@ These configuration options should be placed in `config.h`. Example: |`OLED_SPI_MODE` |`3` (default) |The SPI Mode for the OLED Display (not typically changed). | |`OLED_SPI_DIVISOR` |`2` (default) |The SPI Multiplier to use for the OLED Display. | - - ## 128x64 & Custom sized OLED Displays +## 128x64 & Custom sized OLED Displays The default display size for this feature is 128x32 and all necessary defines are precalculated with that in mind. We have added a define, `OLED_DISPLAY_128X64`, to switch all the values to be used in a 128x64 display, as well as added a custom define, `OLED_DISPLAY_CUSTOM`, that allows you to provide the necessary values to the driver. @@ -219,7 +231,6 @@ These configuration options should be placed in `config.h`. Example: |`OLED_SOURCE_MAP` |`{ 0, ... N }` |Precalculated source array to use for mapping source buffer to target OLED memory in 90 degree rendering. | |`OLED_TARGET_MAP` |`{ 24, ... N }`|Precalculated target array to use for mapping source buffer to target OLED memory in 90 degree rendering. | - ### 90 Degree Rotation - Technical Mumbo Jumbo !> Rotation is unsupported on the SH1106. diff --git a/drivers/oled/oled_driver.c b/drivers/oled/oled_driver.c index da1700909688..40f3a5a4f7d8 100644 --- a/drivers/oled/oled_driver.c +++ b/drivers/oled/oled_driver.c @@ -15,9 +15,9 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#ifdef OLED_DRIVER_SPI +#if defined(OLED_TRANSPORT_SPI) # include "spi_master.h" -#else +#elif defined(OLED_TRANSPORT_I2C) # include "i2c_master.h" #endif #include "oled_driver.h" @@ -158,7 +158,7 @@ uint32_t oled_scroll_timeout; uint16_t oled_update_timeout; #endif -#ifdef OLED_DRIVER_SPI +#if defined(OLED_TRANSPORT_SPI) # ifndef OLED_DC_PIN # error "The OLED driver in SPI needs a D/C pin defined" # endif @@ -171,15 +171,15 @@ uint16_t oled_update_timeout; # ifndef OLED_SPI_DIVISOR # define OLED_SPI_DIVISOR 2 # endif -#else +#elif defined(OLED_TRANSPORT_I2C) # if !defined(OLED_DISPLAY_ADDRESS) # define OLED_DISPLAY_ADDRESS 0x3C # endif #endif // Transmit/Write Funcs. -bool oled_cmd(const uint8_t *data, uint16_t size) { -#ifdef OLED_DRIVER_SPI +__attribute__((weak)) bool oled_cmd(const uint8_t *data, uint16_t size) { +#if defined(OLED_TRANSPORT_SPI) spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR); // Command Mode writePinLow(OLED_DC_PIN); @@ -190,15 +190,27 @@ bool oled_cmd(const uint8_t *data, uint16_t size) { } spi_stop(); return true; -#else +#elif defined(OLED_TRANSPORT_I2C) i2c_status_t status = i2c_transmit((OLED_DISPLAY_ADDRESS << 1), data, size, OLED_I2C_TIMEOUT); return (status == I2C_STATUS_SUCCESS); #endif } -bool oled_cmd_P(const uint8_t *data, uint16_t size) { -#if defined(__AVR__) && !defined(OLED_DRIVER_SPI) +__attribute__((weak)) bool oled_cmd_P(const uint8_t *data, uint16_t size) { +#if defined(__AVR__) +# if defined(OLED_TRANSPORT_SPI) + spi_status_t status = spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR); + // Command Mode + writePinLow(OLED_DC_PIN); + // Send the commands + for (uint16_t i = 0; i < size && status >= 0; i++) { + status = spi_write(pgm_read_byte((const char *)data++))); + if (status) break; + } + spi_stop(); + return (status == SPI_STATUS_SUCCESS); +# elif defined(OLED_TRANSPORT_I2C) i2c_status_t status = i2c_start((OLED_DISPLAY_ADDRESS << 1) | I2C_WRITE, OLED_I2C_TIMEOUT); for (uint16_t i = 0; i < size && status >= 0; i++) { @@ -209,13 +221,14 @@ bool oled_cmd_P(const uint8_t *data, uint16_t size) { i2c_stop(); return (status == I2C_STATUS_SUCCESS); +# endif #else return oled_cmd(data, size); #endif } -bool oled_write_reg(const uint8_t *data, uint16_t size) { -#ifdef OLED_DRIVER_SPI +__attribute__((weak)) bool oled_write_reg(const uint8_t *data, uint16_t size) { +#if defined(OLED_TRANSPORT_SPI) spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR); // Command Mode writePinHigh(OLED_DC_PIN); @@ -226,14 +239,14 @@ bool oled_write_reg(const uint8_t *data, uint16_t size) { } spi_stop(); return true; -#else +#elif defined(OLED_TRANSPORT_I2C) i2c_status_t status = i2c_writeReg((OLED_DISPLAY_ADDRESS << 1), I2C_DATA, data, size, OLED_I2C_TIMEOUT); return (status == I2C_STATUS_SUCCESS); #endif } -void oled_driver_init(void) { -#ifdef OLED_DRIVER_SPI +__attribute__((weak)) void oled_driver_init(void) { +#if defined(OLED_TRANSPORT_SPI) spi_init(); setPinOutput(OLED_CS_PIN); writePinHigh(OLED_CS_PIN); @@ -248,7 +261,7 @@ void oled_driver_init(void) { writePinHigh(OLED_RST_PIN); wait_ms(20); # endif -#else +#elif defined(OLED_TRANSPORT_I2C) i2c_init(); #endif } @@ -263,7 +276,7 @@ static void InvertCharacter(uint8_t *cursor) { } bool oled_init(oled_rotation_t rotation) { -#if defined(USE_I2C) && defined(SPLIT_KEYBOARD) +#if defined(USE_I2C) && defined(SPLIT_KEYBOARD) && defined(OLED_TRANSPORT_I2C) if (!is_keyboard_master()) { return true; } diff --git a/keyboards/adafruit/macropad/rules.mk b/keyboards/adafruit/macropad/rules.mk index 16c6b3c2e1f5..a84e29da3d1f 100644 --- a/keyboards/adafruit/macropad/rules.mk +++ b/keyboards/adafruit/macropad/rules.mk @@ -14,4 +14,4 @@ AUDIO_DRIVER = pwm_hardware ENCODER_ENABLE = yes RGB_MATRIX_ENABLE = yes OLED_ENABLE = yes -OLED_DRIVER = SPI +OLED_TRANSPORT = spi From ab2cc9a0bd00dd5dad722087708d27b9307d5f8f Mon Sep 17 00:00:00 2001 From: Drashna Jael're Date: Mon, 3 Apr 2023 21:33:56 -0700 Subject: [PATCH 07/26] Revert changes to drashna kyria keymap --- keyboards/splitkb/kyria/keymaps/drashna/config.h | 7 ++++--- keyboards/splitkb/kyria/keymaps/drashna/keymap.c | 4 ++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/keyboards/splitkb/kyria/keymaps/drashna/config.h b/keyboards/splitkb/kyria/keymaps/drashna/config.h index 43d4f070f393..2d1fbdcb02d0 100644 --- a/keyboards/splitkb/kyria/keymaps/drashna/config.h +++ b/keyboards/splitkb/kyria/keymaps/drashna/config.h @@ -19,9 +19,10 @@ #define EE_HANDS #ifdef OLED_ENABLE -# define OLED_IC OLED_IC_SH1107 -# undef OLED_DISPLAY_128X64 -# define OLED_DISPLAY_128X128 +# ifdef OLED_DRIVER_SH1107 +# undef OLED_DISPLAY_128X64 +# define OLED_DISPLAY_128X128 +# endif #endif #ifdef RGBLIGHT_ENABLE diff --git a/keyboards/splitkb/kyria/keymaps/drashna/keymap.c b/keyboards/splitkb/kyria/keymaps/drashna/keymap.c index da5b10bfedc5..178d3f2480dc 100644 --- a/keyboards/splitkb/kyria/keymaps/drashna/keymap.c +++ b/keyboards/splitkb/kyria/keymaps/drashna/keymap.c @@ -126,7 +126,11 @@ void render_oled_title(bool side) { } oled_rotation_t oled_init_keymap(oled_rotation_t rotation) { +# ifdef OLED_DRIVER_SH1107 return OLED_ROTATION_0; +# else + return OLED_ROTATION_180; +# endif } void oled_render_large_display(bool side) { From 2b3f3850850ae7af2188922d369bf48c2333dffb Mon Sep 17 00:00:00 2001 From: Drashna Jael're Date: Thu, 13 Apr 2023 16:54:39 -0700 Subject: [PATCH 08/26] Add display clock and vcom detect settings --- drivers/oled/oled_driver.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/oled/oled_driver.c b/drivers/oled/oled_driver.c index 40f3a5a4f7d8..f7b2ae36ed54 100644 --- a/drivers/oled/oled_driver.c +++ b/drivers/oled/oled_driver.c @@ -100,6 +100,14 @@ along with this program. If not, see . #ifndef OLED_BLOCK_SIZE # define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) #endif +// Default display clock +#if !defined(OLED_DISPLAY_CLOCK) +# define OLED_DISPLAY_CLOCK 0x80 +#endif +// Default VCom Detect value +#if !defined(OLED_VCOM_DETECT) +# define OLED_VCOM_DETECT 0x20 +#endif #define OLED_ALL_BLOCKS_MASK (((((OLED_BLOCK_TYPE)1 << (OLED_BLOCK_COUNT - 1)) - 1) << 1) | 1) @@ -294,7 +302,7 @@ bool oled_init(oled_rotation_t rotation) { I2C_CMD, DISPLAY_OFF, DISPLAY_CLOCK, - 0x80, + OLED_DISPLAY_CLOCK, MULTIPLEX_RATIO, #if OLED_IC_COM_PINS_ARE_COLUMNS OLED_DISPLAY_WIDTH - 1, @@ -307,6 +315,8 @@ bool oled_init(oled_rotation_t rotation) { #else DISPLAY_START_LINE | 0x00, #endif + VCOM_DETECT, + OLED_VCOM_DETECT, CHARGE_PUMP, 0x14, #if OLED_IC_HAS_HORIZONTAL_MODE From 8e7e850d14459ec862564bb32ef57da88f82a322 Mon Sep 17 00:00:00 2001 From: Drashna Jael're Date: Thu, 13 Apr 2023 17:20:59 -0700 Subject: [PATCH 09/26] Improve documentation --- docs/feature_oled_driver.md | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/docs/feature_oled_driver.md b/docs/feature_oled_driver.md index 429966771738..bf2d72168d79 100644 --- a/docs/feature_oled_driver.md +++ b/docs/feature_oled_driver.md @@ -173,22 +173,24 @@ These configuration options should be placed in `config.h`. Example: #define OLED_BRIGHTNESS 128 ``` -|Define |Default |Description | -|---------------------------|-----------------|--------------------------------------------------------------------------------------------------------------------------| -|`OLED_FONT_H` |`"glcdfont.c"` |The font code file to use for custom fonts | -|`OLED_FONT_START` |`0` |The starting character index for custom fonts | -|`OLED_FONT_END` |`223` |The ending character index for custom fonts | -|`OLED_FONT_WIDTH` |`6` |The font width | -|`OLED_FONT_HEIGHT` |`8` |The font height (untested) | -|`OLED_TIMEOUT` |`60000` |Turns off the OLED screen after 60000ms of screen update inactivity. Helps reduce OLED Burn-in. Set to 0 to disable. | -|`OLED_FADE_OUT` |*Not defined* |Enables fade out animation. Use together with `OLED_TIMEOUT`. | -|`OLED_FADE_OUT_INTERVAL` |`0` |The speed of fade out animation, from 0 to 15. Larger values are slower. | -|`OLED_SCROLL_TIMEOUT` |`0` |Scrolls the OLED screen after 0ms of OLED inactivity. Helps reduce OLED Burn-in. Set to 0 to disable. | -|`OLED_SCROLL_TIMEOUT_RIGHT`|*Not defined* |Scroll timeout direction is right when defined, left when undefined. | -|`OLED_IC` |`OLED_IC_SSD1306`|Set to `OLED_IC_SH1106` or `OLED_IC_SH1107` if the corresponding controller chip is used. | -|`OLED_COLUMN_OFFSET` |`0` |Shift output to the right this many pixels.
Useful for 128x64 displays centered on a 132x64 SH1106 IC. | -|`OLED_BRIGHTNESS` |`255` |The default brightness level of the OLED, from 0 to 255. | -|`OLED_UPDATE_INTERVAL` |`0` |Set the time interval for updating the OLED display in ms. This will improve the matrix scan rate. | +|Define |Default |Description | +|---------------------------|-------------------------------|---------------------------------------------------------------------------------------------------------------------| +|`OLED_BRIGHTNESS` |`255` |The default brightness level of the OLED, from 0 to 255. | +|`OLED_COLUMN_OFFSET` |`0` |Shift output to the right this many pixels.
Useful for 128x64 displays centered on a 132x64 SH1106 IC. | +|`OLED_DISPLAY_CLOCK` |`0x80` |Set the display clock divide ratio/oscillator frequency. | +|`OLED_FONT_H` |`"glcdfont.c"` |The font code file to use for custom fonts | +|`OLED_FONT_START` |`0` |The starting character index for custom fonts | +|`OLED_FONT_END` |`223` |The ending character index for custom fonts | +|`OLED_FONT_WIDTH` |`6` |The font width | +|`OLED_FONT_HEIGHT` |`8` |The font height (untested) | +|`OLED_IC` |`OLED_IC_SSD1306` |Set to `OLED_IC_SH1106` or `OLED_IC_SH1107` if the corresponding controller chip is used. | +|`OLED_FADE_OUT` |*Not defined* |Enables fade out animation. Use together with `OLED_TIMEOUT`. | +|`OLED_FADE_OUT_INTERVAL` |`0` |The speed of fade out animation, from 0 to 15. Larger values are slower. | +|`OLED_SCROLL_TIMEOUT` |`0` |Scrolls the OLED screen after 0ms of OLED inactivity. Helps reduce OLED Burn-in. Set to 0 to disable. | +|`OLED_SCROLL_TIMEOUT_RIGHT`|*Not defined* |Scroll timeout direction is right when defined, left when undefined. | +|`OLED_TIMEOUT` |`60000` |Turns off the OLED screen after 60000ms of screen update inactivity. Helps reduce OLED Burn-in. Set to 0 to disable. | +|`OLED_UPDATE_INTERVAL` |`0` (`50` for split keyboards) |Set the time interval for updating the OLED display in ms. This will improve the matrix scan rate. | +|`OLED_UPDATE_PROCESS_LIMIT'|`1` |Set the number of dirty blocks to render per loop. Increasing may degrade performance. | ### I2C Configuration |Define |Default |Description | From 16e239e9a3f2f1ccbf22f886b3444c0504157c92 Mon Sep 17 00:00:00 2001 From: Drashna Jael're Date: Thu, 13 Apr 2023 17:29:10 -0700 Subject: [PATCH 10/26] Make pre-charge configurable and move vcom detect to proper location --- drivers/oled/oled_driver.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/oled/oled_driver.c b/drivers/oled/oled_driver.c index f7b2ae36ed54..4f7636bd36bd 100644 --- a/drivers/oled/oled_driver.c +++ b/drivers/oled/oled_driver.c @@ -104,10 +104,14 @@ along with this program. If not, see . #if !defined(OLED_DISPLAY_CLOCK) # define OLED_DISPLAY_CLOCK 0x80 #endif -// Default VCom Detect value +// Default VCOMH deselect value #if !defined(OLED_VCOM_DETECT) # define OLED_VCOM_DETECT 0x20 #endif +#if !defined(OLED_PRE_CHARGE_PERIOD) +# define OLED_PRE_CHARGE_PERIOD 0xF1 +#endif + #define OLED_ALL_BLOCKS_MASK (((((OLED_BLOCK_TYPE)1 << (OLED_BLOCK_COUNT - 1)) - 1) << 1) | 1) @@ -315,8 +319,6 @@ bool oled_init(oled_rotation_t rotation) { #else DISPLAY_START_LINE | 0x00, #endif - VCOM_DETECT, - OLED_VCOM_DETECT, CHARGE_PUMP, 0x14, #if OLED_IC_HAS_HORIZONTAL_MODE @@ -351,7 +353,7 @@ bool oled_init(oled_rotation_t rotation) { } } - static const uint8_t PROGMEM display_setup2[] = {I2C_CMD, COM_PINS, OLED_COM_PINS, CONTRAST, OLED_BRIGHTNESS, PRE_CHARGE_PERIOD, 0xF1, VCOM_DETECT, 0x20, DISPLAY_ALL_ON_RESUME, NORMAL_DISPLAY, DEACTIVATE_SCROLL, DISPLAY_ON}; + static const uint8_t PROGMEM display_setup2[] = {I2C_CMD, COM_PINS, OLED_COM_PINS, CONTRAST, OLED_BRIGHTNESS, PRE_CHARGE_PERIOD, OLED_PRE_CHARGE_PERIOD, VCOM_DETECT, OLED_VCOM_DETECT, DISPLAY_ALL_ON_RESUME, NORMAL_DISPLAY, DEACTIVATE_SCROLL, DISPLAY_ON}; if (!oled_cmd_P(display_setup2, ARRAY_SIZE(display_setup2))) { print("display_setup2 failed\n"); return false; From 7531220997f6c38dfaa6e99b062798c9d1630934 Mon Sep 17 00:00:00 2001 From: Drashna Jael're Date: Sat, 15 Apr 2023 05:37:32 -0700 Subject: [PATCH 11/26] fix userspace changes --- users/drashna/oled/oled.mk | 9 --------- users/drashna/rules.mk | 6 ------ 2 files changed, 15 deletions(-) delete mode 100644 users/drashna/oled/oled.mk diff --git a/users/drashna/oled/oled.mk b/users/drashna/oled/oled.mk deleted file mode 100644 index 25dca0a3badd..000000000000 --- a/users/drashna/oled/oled.mk +++ /dev/null @@ -1,9 +0,0 @@ -CUSTOM_OLED_DRIVER ?= yes -ifeq ($(strip $(CUSTOM_OLED_DRIVER)), yes) - OPT_DEFS += -DCUSTOM_OLED_DRIVER - SRC += $(USER_PATH)/oled/oled_stuff.c -endif -ifeq ($(strip $(OLED_DISPLAY_TEST)), yes) - OPT_DEFS += -DOLED_DISPLAY_TEST -endif -DEFERRED_EXEC_ENABLE = yes diff --git a/users/drashna/rules.mk b/users/drashna/rules.mk index 43186b024ae4..276a6e487e35 100644 --- a/users/drashna/rules.mk +++ b/users/drashna/rules.mk @@ -94,12 +94,6 @@ endif CUSTOM_OLED_DRIVER ?= yes ifeq ($(strip $(OLED_ENABLE)), yes) - ifeq ($(strip $(OLED_DRIVER)), custom) - OPT_DEFS += -DOLED_ENABLE \ - -DOLED_DRIVER_SH1107 - SRC += $(USER_PATH)/oled/sh110x.c - QUANTUM_LIB_SRC += i2c_master.c - endif ifeq ($(strip $(CUSTOM_OLED_DRIVER)), yes) OPT_DEFS += -DCUSTOM_OLED_DRIVER SRC += $(USER_PATH)/oled/oled_stuff.c From 9981e5d27dd908bbf1f70fe9ac8a92f857b107ac Mon Sep 17 00:00:00 2001 From: Drashna Jael're Date: Sun, 30 Apr 2023 00:17:40 -0700 Subject: [PATCH 12/26] Remove changes to drashna code --- .../dilemma/3x5_2/keymaps/drashna/rules.mk | 1 + .../5x6_right/keymaps/drashna/rules.mk | 1 + .../splitkb/kyria/keymaps/drashna/rules.mk | 1 + users/drashna/oled/oled_config.h | 21 + users/drashna/oled/sh110x.c | 860 ++++++++++++++++++ users/drashna/rules.mk | 6 + 6 files changed, 890 insertions(+) create mode 100644 users/drashna/oled/sh110x.c diff --git a/keyboards/bastardkb/dilemma/3x5_2/keymaps/drashna/rules.mk b/keyboards/bastardkb/dilemma/3x5_2/keymaps/drashna/rules.mk index a1be9cb8fd14..41d73bc49ae1 100644 --- a/keyboards/bastardkb/dilemma/3x5_2/keymaps/drashna/rules.mk +++ b/keyboards/bastardkb/dilemma/3x5_2/keymaps/drashna/rules.mk @@ -5,3 +5,4 @@ CONSOLE_ENABLE = yes KEYLOGGER_ENABLE = no WPM_ENABLE = yes OLED_ENABLE = yes +OLED_DRIVER = custom diff --git a/keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/rules.mk b/keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/rules.mk index 85abb142396c..acec28fa6e23 100644 --- a/keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/rules.mk +++ b/keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/rules.mk @@ -5,6 +5,7 @@ CUSTOM_BOOTMAGIC_ENABLE = no CUSTOM_UNICODE_ENABLE = no HAPTIC_ENABLE = no OLED_ENABLE = no +OLED_DRIVER = custom RGBLIGHT_ENABLE = no SWAP_HANDS_ENABLE = no TAP_DANCE_ENABLE = no diff --git a/keyboards/splitkb/kyria/keymaps/drashna/rules.mk b/keyboards/splitkb/kyria/keymaps/drashna/rules.mk index 8ff7b010a18b..882a3fba092a 100644 --- a/keyboards/splitkb/kyria/keymaps/drashna/rules.mk +++ b/keyboards/splitkb/kyria/keymaps/drashna/rules.mk @@ -20,6 +20,7 @@ ifeq ($(strip $(KEYBOARD)), splitkb/kyria/rev1/proton_c) ENCODER_MAP_ENABLE = yes AUTOCORRECT_ENABLE = yes CAPS_WORD_ENABLE = yes + OLED_DRIVER = custom else LTO_ENABLE = yes BOOTLOADER = qmk-hid diff --git a/users/drashna/oled/oled_config.h b/users/drashna/oled/oled_config.h index d8787259f488..c46c0c39ce2f 100644 --- a/users/drashna/oled/oled_config.h +++ b/users/drashna/oled/oled_config.h @@ -40,3 +40,24 @@ #define OLED_LOGO_SCIFI // # define OLED_LOGO_SETS3N // # define OLED_LOGO_SKEEB + +#ifdef OLED_DRIVER_SH1107 +# define OLED_DISPLAY_CUSTOM +# define OLED_IC_SH1107 2 +# define OLED_DISPLAY_128X128 +# define OLED_DISPLAY_WIDTH 128 +# define OLED_DISPLAY_HEIGHT 128 +# define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH) +# define OLED_BLOCK_TYPE uint32_t +# define OLED_SOURCE_MAP \ + { 0, 8, 16, 24, 32, 40, 48, 56 } +# define OLED_TARGET_MAP \ + { 56, 48, 40, 32, 24, 16, 8, 0 } +# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) +# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) +# define OLED_COM_PINS COM_PINS_ALT +# define OLED_IC OLED_IC_SH1107 +# ifndef OLED_BRIGHTNESS +# define OLED_BRIGHTNESS 50 +# endif +#endif diff --git a/users/drashna/oled/sh110x.c b/users/drashna/oled/sh110x.c new file mode 100644 index 000000000000..f96a93a8973a --- /dev/null +++ b/users/drashna/oled/sh110x.c @@ -0,0 +1,860 @@ +/* +Copyright 2019 Ryan Caltabiano + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include "i2c_master.h" +#include "oled_driver.h" +#include OLED_FONT_H +#include "timer.h" +#include "print.h" + +#include + +#include "progmem.h" + +#include "keyboard.h" + +// for SH1107: https://www.displayfuture.com/Display/datasheet/controller/SH1107.pdf + +// Fundamental Commands +#define CONTRAST 0x81 +#define DISPLAY_ALL_ON 0xA5 +#define DISPLAY_ALL_ON_RESUME 0xA4 +#define NORMAL_DISPLAY 0xA6 +#define INVERT_DISPLAY 0xA7 +#define DISPLAY_ON 0xAF +#define DISPLAY_OFF 0xAE +#define NOP 0xE3 + +// Scrolling Commands +#define ACTIVATE_SCROLL 0x2F +#define DEACTIVATE_SCROLL 0x2E +#define SCROLL_RIGHT 0x26 +#define SCROLL_LEFT 0x27 +#define SCROLL_RIGHT_UP 0x29 +#define SCROLL_LEFT_UP 0x2A + +// Addressing Setting Commands +#define MEMORY_MODE 0x20 +#define COLUMN_ADDR 0x21 +#define PAGE_ADDR 0x22 +#define PAM_SETCOLUMN_LSB 0x00 +#define PAM_SETCOLUMN_MSB 0x10 +#define PAM_PAGE_ADDR 0xB0 // 0xb0 -- 0xb7 + +// Hardware Configuration Commands +#define DISPLAY_START_LINE 0x40 +#define SEGMENT_REMAP 0xA0 +#define SEGMENT_REMAP_INV 0xA1 +#define MULTIPLEX_RATIO 0xA8 +#define COM_SCAN_INC 0xC0 +#define COM_SCAN_DEC 0xC8 +#define DISPLAY_OFFSET 0xD3 +#define COM_PINS 0xDA +#define COM_PINS_SEQ 0x02 +#define COM_PINS_ALT 0x12 +#define COM_PINS_SEQ_LR 0x22 +#define COM_PINS_ALT_LR 0x32 + +// Timing & Driving Commands +#define DISPLAY_CLOCK 0xD5 +#define PRE_CHARGE_PERIOD 0xD9 +#define VCOM_DETECT 0xDB + +// Advance Graphic Commands +#define FADE_BLINK 0x23 +#define ENABLE_FADE 0x20 +#define ENABLE_BLINK 0x30 + +// Charge Pump Commands +#define CHARGE_PUMP 0x8D + +// Commands specific to the SH1107 chip +#define SH1107_DISPLAY_START_LINE 0xDC +#define SH1107_MEMORY_MODE_PAGE 0x20 +#define SH1107_MEMORY_MODE_VERTICAL 0x21 + +// Misc defines +#ifndef OLED_BLOCK_COUNT +# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) +#endif +#ifndef OLED_BLOCK_SIZE +# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) +#endif + +#define OLED_ALL_BLOCKS_MASK (((((OLED_BLOCK_TYPE)1 << (OLED_BLOCK_COUNT - 1)) - 1) << 1) | 1) + +#ifndef OLED_COM_PIN_COUNT +# define OLED_COM_PIN_COUNT 128 +#endif + +#ifndef OLED_COM_PIN_OFFSET +# define OLED_COM_PIN_OFFSET 0 +#endif + +// i2c defines +#define I2C_CMD 0x00 +#define I2C_DATA 0x40 +#if defined(__AVR__) +# define I2C_TRANSMIT_P(data) i2c_transmit_P((OLED_DISPLAY_ADDRESS << 1), &data[0], sizeof(data), OLED_I2C_TIMEOUT) +#else // defined(__AVR__) +# define I2C_TRANSMIT_P(data) i2c_transmit((OLED_DISPLAY_ADDRESS << 1), &data[0], sizeof(data), OLED_I2C_TIMEOUT) +#endif // defined(__AVR__) +#define I2C_TRANSMIT(data) i2c_transmit((OLED_DISPLAY_ADDRESS << 1), &data[0], sizeof(data), OLED_I2C_TIMEOUT) +#define I2C_WRITE_REG(mode, data, size) i2c_writeReg((OLED_DISPLAY_ADDRESS << 1), mode, data, size, OLED_I2C_TIMEOUT) + +#define HAS_FLAGS(bits, flags) ((bits & flags) == flags) + +// Display buffer's is the same as the OLED memory layout +// this is so we don't end up with rounding errors with +// parts of the display unusable or don't get cleared correctly +// and also allows for drawing & inverting +uint8_t oled_buffer[OLED_MATRIX_SIZE]; +uint8_t *oled_cursor; +OLED_BLOCK_TYPE oled_dirty = 0; +bool oled_initialized = false; +bool oled_active = false; +bool oled_scrolling = false; +bool oled_inverted = false; +uint8_t oled_brightness = OLED_BRIGHTNESS; +oled_rotation_t oled_rotation = 0; +uint8_t oled_rotation_width = 0; +uint8_t oled_scroll_speed = 0; // this holds the speed after being remapped to ssd1306 internal values +uint8_t oled_scroll_start = 0; +uint8_t oled_scroll_end = 7; +#if OLED_TIMEOUT > 0 +uint32_t oled_timeout; +#endif +#if OLED_SCROLL_TIMEOUT > 0 +uint32_t oled_scroll_timeout; +#endif +#if OLED_UPDATE_INTERVAL > 0 +uint16_t oled_update_timeout; +#endif + +// Internal variables to reduce math instructions + +#if defined(__AVR__) +// identical to i2c_transmit, but for PROGMEM since all initialization is in PROGMEM arrays currently +// probably should move this into i2c_master... +static i2c_status_t i2c_transmit_P(uint8_t address, const uint8_t *data, uint16_t length, uint16_t timeout) { + i2c_status_t status = i2c_start(address | I2C_WRITE, timeout); + + for (uint16_t i = 0; i < length && status >= 0; i++) { + status = i2c_write(pgm_read_byte((const char *)data++), timeout); + if (status) break; + } + + i2c_stop(); + + return status; +} +#endif + +// Flips the rendering bits for a character at the current cursor position +static void InvertCharacter(uint8_t *cursor) { + const uint8_t *end = cursor + OLED_FONT_WIDTH; + while (cursor < end) { + *cursor = ~(*cursor); + cursor++; + } +} + +bool oled_init(oled_rotation_t rotation) { +#if defined(USE_I2C) && defined(SPLIT_KEYBOARD) + if (!is_keyboard_master()) { + return true; + } +#endif + + oled_rotation = oled_init_user(oled_init_kb(rotation)); + if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { + oled_rotation_width = OLED_DISPLAY_WIDTH; + } else { + oled_rotation_width = OLED_DISPLAY_HEIGHT; + } + i2c_init(); + + static const uint8_t PROGMEM display_setup1[] = { + I2C_CMD, + DISPLAY_OFF, + DISPLAY_CLOCK, + 0x80, + MULTIPLEX_RATIO, + OLED_DISPLAY_WIDTH - 1, + SH1107_DISPLAY_START_LINE, + 0x00, + CHARGE_PUMP, + 0x14, + SH1107_MEMORY_MODE_PAGE, + }; + if (I2C_TRANSMIT_P(display_setup1) != I2C_STATUS_SUCCESS) { + print("oled_init cmd set 1 failed\n"); + return false; + } + + if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_180)) { + static const uint8_t PROGMEM display_normal[] = { + I2C_CMD, + SEGMENT_REMAP_INV, + COM_SCAN_DEC, + DISPLAY_OFFSET, + OLED_COM_PIN_OFFSET, + }; + if (I2C_TRANSMIT_P(display_normal) != I2C_STATUS_SUCCESS) { + print("oled_init cmd normal rotation failed\n"); + return false; + } + } else { + static const uint8_t PROGMEM display_flipped[] = { + I2C_CMD, + SEGMENT_REMAP, + COM_SCAN_INC, + DISPLAY_OFFSET, + (OLED_COM_PIN_COUNT - OLED_COM_PIN_OFFSET) % OLED_COM_PIN_COUNT, + }; + if (I2C_TRANSMIT_P(display_flipped) != I2C_STATUS_SUCCESS) { + print("display_flipped failed\n"); + return false; + } + } + + static const uint8_t PROGMEM display_setup2[] = { + I2C_CMD, COM_PINS, + OLED_COM_PINS, + CONTRAST, OLED_BRIGHTNESS, + PRE_CHARGE_PERIOD, 0x22, + VCOM_DETECT, 0x35, + DISPLAY_ALL_ON_RESUME, + NORMAL_DISPLAY, + DEACTIVATE_SCROLL, + DISPLAY_ON + }; + if (I2C_TRANSMIT_P(display_setup2) != I2C_STATUS_SUCCESS) { + print("display_setup2 failed\n"); + return false; + } + +#if OLED_TIMEOUT > 0 + oled_timeout = timer_read32() + OLED_TIMEOUT; +#endif +#if OLED_SCROLL_TIMEOUT > 0 + oled_scroll_timeout = timer_read32() + OLED_SCROLL_TIMEOUT; +#endif + + oled_clear(); + oled_initialized = true; + oled_active = true; + oled_scrolling = false; + return true; +} + +__attribute__((weak)) oled_rotation_t oled_init_kb(oled_rotation_t rotation) { + return rotation; +} +__attribute__((weak)) oled_rotation_t oled_init_user(oled_rotation_t rotation) { + return rotation; +} + +void oled_clear(void) { + memset(oled_buffer, 0, sizeof(oled_buffer)); + oled_cursor = &oled_buffer[0]; + oled_dirty = OLED_ALL_BLOCKS_MASK; +} + +static void calc_bounds(uint8_t update_start, uint8_t *cmd_array) { + // Calculate commands to set memory addressing bounds. + uint8_t start_page = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_WIDTH; + uint8_t start_column = OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_WIDTH; + // Commands for Page Addressing Mode. Sets starting page and column; has no end bound. + // Column value must be split into high and low nybble and sent as two commands. + cmd_array[0] = PAM_PAGE_ADDR | start_page; + cmd_array[1] = PAM_SETCOLUMN_LSB | ((OLED_COLUMN_OFFSET + start_column) & 0x0f); + cmd_array[2] = PAM_SETCOLUMN_MSB | ((OLED_COLUMN_OFFSET + start_column) >> 4 & 0x0f); +} + +static void calc_bounds_90(uint8_t update_start, uint8_t *cmd_array) { + // Block numbering starts from the bottom left corner, going up and then to + // the right. The controller needs the page and column numbers for the top + // left and bottom right corners of that block. + + // Total number of pages across the screen height. + const uint8_t height_in_pages = OLED_DISPLAY_HEIGHT / 8; + + // Difference of starting page numbers for adjacent blocks; may be 0 if + // blocks are large enough to occupy one or more whole 8px columns. + const uint8_t page_inc_per_block = OLED_BLOCK_SIZE % OLED_DISPLAY_HEIGHT / 8; + + // Top page number for a block which is at the bottom edge of the screen. + const uint8_t bottom_block_top_page = (height_in_pages - page_inc_per_block) % height_in_pages; + + // Only the Page Addressing Mode is supported + uint8_t start_page = bottom_block_top_page - (OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_HEIGHT / 8); + uint8_t start_column = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_HEIGHT * 8; + cmd_array[0] = PAM_PAGE_ADDR | start_page; + cmd_array[1] = PAM_SETCOLUMN_LSB | ((OLED_COLUMN_OFFSET + start_column) & 0x0f); + cmd_array[2] = PAM_SETCOLUMN_MSB | ((OLED_COLUMN_OFFSET + start_column) >> 4 & 0x0f); +} + +uint8_t crot(uint8_t a, int8_t n) { + const uint8_t mask = 0x7; + n &= mask; + return a << n | a >> (-n & mask); +} + +static void rotate_90(const uint8_t *src, uint8_t *dest) { + for (uint8_t i = 0, shift = 7; i < 8; ++i, --shift) { + uint8_t selector = (1 << i); + for (uint8_t j = 0; j < 8; ++j) { + dest[i] |= crot(src[j] & selector, shift - (int8_t)j); + } + } +} + +void oled_render(void) { + // Do we have work to do? + oled_dirty &= OLED_ALL_BLOCKS_MASK; + if (!oled_dirty || !oled_initialized || oled_scrolling) { + return; + } + + // Turn on display if it is off + oled_on(); + + uint8_t update_start = 0; + uint8_t num_processed = 0; + while (oled_dirty && num_processed++ < OLED_UPDATE_PROCESS_LIMIT) { // render all dirty blocks (up to the configured limit) + // Find next dirty block + while (!(oled_dirty & ((OLED_BLOCK_TYPE)1 << update_start))) { + ++update_start; + } + + // Set column & page position + static uint8_t display_start[] = {I2C_CMD, PAM_PAGE_ADDR, PAM_SETCOLUMN_LSB, PAM_SETCOLUMN_MSB}; + if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { + calc_bounds(update_start, &display_start[1]); // Offset from I2C_CMD byte at the start + } else { + calc_bounds_90(update_start, &display_start[1]); // Offset from I2C_CMD byte at the start + } + + // Send column & page position + if (I2C_TRANSMIT(display_start) != I2C_STATUS_SUCCESS) { + print("oled_render offset command failed\n"); + return; + } + + if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { + // Send render data chunk as is + if (I2C_WRITE_REG(I2C_DATA, &oled_buffer[OLED_BLOCK_SIZE * update_start], OLED_BLOCK_SIZE) != I2C_STATUS_SUCCESS) { + print("oled_render data failed\n"); + return; + } + } else { + // Rotate the render chunks + const static uint8_t source_map[] = OLED_SOURCE_MAP; + const static uint8_t target_map[] = OLED_TARGET_MAP; + + static uint8_t temp_buffer[OLED_BLOCK_SIZE]; + memset(temp_buffer, 0, sizeof(temp_buffer)); + for (uint8_t i = 0; i < sizeof(source_map); ++i) { + rotate_90(&oled_buffer[OLED_BLOCK_SIZE * update_start + source_map[i]], &temp_buffer[target_map[i]]); + } + + // For SH1106 or SH1107 the data chunk must be split into separate pieces for each page + const uint8_t columns_in_block = (OLED_BLOCK_SIZE + OLED_DISPLAY_HEIGHT - 1) / OLED_DISPLAY_HEIGHT * 8; + const uint8_t num_pages = OLED_BLOCK_SIZE / columns_in_block; + for (uint8_t i = 0; i < num_pages; ++i) { + // Send column & page position for all pages except the first one + if (i > 0) { + display_start[1]++; + if (I2C_TRANSMIT(display_start) != I2C_STATUS_SUCCESS) { + print("oled_render offset command failed\n"); + return; + } + } + // Send data for the page + if (I2C_WRITE_REG(I2C_DATA, &temp_buffer[columns_in_block * i], columns_in_block) != I2C_STATUS_SUCCESS) { + print("oled_render90 data failed\n"); + return; + } + } + } + + // Clear dirty flag + oled_dirty &= ~((OLED_BLOCK_TYPE)1 << update_start); + } +} + +void oled_set_cursor(uint8_t col, uint8_t line) { + uint16_t index = line * oled_rotation_width + col * OLED_FONT_WIDTH; + + // Out of bounds? + if (index >= OLED_MATRIX_SIZE) { + index = 0; + } + + oled_cursor = &oled_buffer[index]; +} + +void oled_advance_page(bool clearPageRemainder) { + uint16_t index = oled_cursor - &oled_buffer[0]; + uint8_t remaining = oled_rotation_width - (index % oled_rotation_width); + + if (clearPageRemainder) { + // Remaining Char count + remaining = remaining / OLED_FONT_WIDTH; + + // Write empty character until next line + while (remaining--) + oled_write_char(' ', false); + } else { + // Next page index out of bounds? + if (index + remaining >= OLED_MATRIX_SIZE) { + index = 0; + remaining = 0; + } + + oled_cursor = &oled_buffer[index + remaining]; + } +} + +void oled_advance_char(void) { + uint16_t nextIndex = oled_cursor - &oled_buffer[0] + OLED_FONT_WIDTH; + uint8_t remainingSpace = oled_rotation_width - (nextIndex % oled_rotation_width); + + // Do we have enough space on the current line for the next character + if (remainingSpace < OLED_FONT_WIDTH) { + nextIndex += remainingSpace; + } + + // Did we go out of bounds + if (nextIndex >= OLED_MATRIX_SIZE) { + nextIndex = 0; + } + + // Update cursor position + oled_cursor = &oled_buffer[nextIndex]; +} + +// Main handler that writes character data to the display buffer +void oled_write_char(const char data, bool invert) { + // Advance to the next line if newline + if (data == '\n') { + // Old source wrote ' ' until end of line... + oled_advance_page(true); + return; + } + + if (data == '\r') { + oled_advance_page(false); + return; + } + + // copy the current render buffer to check for dirty after + static uint8_t oled_temp_buffer[OLED_FONT_WIDTH]; + memcpy(&oled_temp_buffer, oled_cursor, OLED_FONT_WIDTH); + + _Static_assert(sizeof(font) >= ((OLED_FONT_END + 1 - OLED_FONT_START) * OLED_FONT_WIDTH), "OLED_FONT_END references outside array"); + + // set the reder buffer data + uint8_t cast_data = (uint8_t)data; // font based on unsigned type for index + if (cast_data < OLED_FONT_START || cast_data > OLED_FONT_END) { + memset(oled_cursor, 0x00, OLED_FONT_WIDTH); + } else { + const uint8_t *glyph = &font[(cast_data - OLED_FONT_START) * OLED_FONT_WIDTH]; + memcpy_P(oled_cursor, glyph, OLED_FONT_WIDTH); + } + + // Invert if needed + if (invert) { + InvertCharacter(oled_cursor); + } + + // Dirty check + if (memcmp(&oled_temp_buffer, oled_cursor, OLED_FONT_WIDTH)) { + uint16_t index = oled_cursor - &oled_buffer[0]; + oled_dirty |= ((OLED_BLOCK_TYPE)1 << (index / OLED_BLOCK_SIZE)); + // Edgecase check if the written data spans the 2 chunks + oled_dirty |= ((OLED_BLOCK_TYPE)1 << ((index + OLED_FONT_WIDTH - 1) / OLED_BLOCK_SIZE)); + } + + // Finally move to the next char + oled_advance_char(); +} + +void oled_write(const char *data, bool invert) { + const char *end = data + strlen(data); + while (data < end) { + oled_write_char(*data, invert); + data++; + } +} + +void oled_write_ln(const char *data, bool invert) { + oled_write(data, invert); + oled_advance_page(true); +} + +void oled_pan(bool left) { + uint16_t i = 0; + for (uint16_t y = 0; y < OLED_DISPLAY_HEIGHT / 8; y++) { + if (left) { + for (uint16_t x = 0; x < OLED_DISPLAY_WIDTH - 1; x++) { + i = y * OLED_DISPLAY_WIDTH + x; + oled_buffer[i] = oled_buffer[i + 1]; + } + } else { + for (uint16_t x = OLED_DISPLAY_WIDTH - 1; x > 0; x--) { + i = y * OLED_DISPLAY_WIDTH + x; + oled_buffer[i] = oled_buffer[i - 1]; + } + } + } + oled_dirty = OLED_ALL_BLOCKS_MASK; +} + +void oled_pan_section(bool left, uint16_t y_start, uint16_t y_end, uint16_t x_start, uint16_t x_end) { + uint16_t i = 0; + for (uint16_t y = y_start; y < y_end; y++) { + if (left) { + for (uint16_t x = x_start; x < x_end - 1; x++) { + i = y * OLED_DISPLAY_WIDTH + x; + oled_buffer[i] = oled_buffer[i + 1]; + oled_dirty |= ((OLED_BLOCK_TYPE)1 << (i / OLED_BLOCK_SIZE)); + } + } else { + for (uint16_t x = x_end - 1; x > 0; x--) { + i = y * OLED_DISPLAY_WIDTH + x; + oled_buffer[i] = oled_buffer[i - 1]; + oled_dirty |= ((OLED_BLOCK_TYPE)1 << (i / OLED_BLOCK_SIZE)); + } + } + } +} + +oled_buffer_reader_t oled_read_raw(uint16_t start_index) { + if (start_index > OLED_MATRIX_SIZE) start_index = OLED_MATRIX_SIZE; + oled_buffer_reader_t ret_reader; + ret_reader.current_element = &oled_buffer[start_index]; + ret_reader.remaining_element_count = OLED_MATRIX_SIZE - start_index; + return ret_reader; +} + +void oled_write_raw_byte(const char data, uint16_t index) { + if (index > OLED_MATRIX_SIZE) index = OLED_MATRIX_SIZE; + if (oled_buffer[index] == data) return; + oled_buffer[index] = data; + oled_dirty |= ((OLED_BLOCK_TYPE)1 << (index / OLED_BLOCK_SIZE)); +} + +void oled_write_raw(const char *data, uint16_t size) { + uint16_t cursor_start_index = oled_cursor - &oled_buffer[0]; + if ((size + cursor_start_index) > OLED_MATRIX_SIZE) size = OLED_MATRIX_SIZE - cursor_start_index; + for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) { + uint8_t c = *data++; + if (oled_buffer[i] == c) continue; + oled_buffer[i] = c; + oled_dirty |= ((OLED_BLOCK_TYPE)1 << (i / OLED_BLOCK_SIZE)); + } +} + +void oled_write_pixel(uint8_t x, uint8_t y, bool on) { + if (x >= oled_rotation_width) { + return; + } + uint16_t index = x + (y / 8) * oled_rotation_width; + if (index >= OLED_MATRIX_SIZE) { + return; + } + uint8_t data = oled_buffer[index]; + if (on) { + data |= (1 << (y % 8)); + } else { + data &= ~(1 << (y % 8)); + } + if (oled_buffer[index] != data) { + oled_buffer[index] = data; + oled_dirty |= ((OLED_BLOCK_TYPE)1 << (index / OLED_BLOCK_SIZE)); + } +} + +#if defined(__AVR__) +void oled_write_P(const char *data, bool invert) { + uint8_t c = pgm_read_byte(data); + while (c != 0) { + oled_write_char(c, invert); + c = pgm_read_byte(++data); + } +} + +void oled_write_ln_P(const char *data, bool invert) { + oled_write_P(data, invert); + oled_advance_page(true); +} + +void oled_write_raw_P(const char *data, uint16_t size) { + uint16_t cursor_start_index = oled_cursor - &oled_buffer[0]; + if ((size + cursor_start_index) > OLED_MATRIX_SIZE) size = OLED_MATRIX_SIZE - cursor_start_index; + for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) { + uint8_t c = pgm_read_byte(data++); + if (oled_buffer[i] == c) continue; + oled_buffer[i] = c; + oled_dirty |= ((OLED_BLOCK_TYPE)1 << (i / OLED_BLOCK_SIZE)); + } +} +#endif // defined(__AVR__) + +bool oled_on(void) { + if (!oled_initialized) { + return oled_active; + } + +#if OLED_TIMEOUT > 0 + oled_timeout = timer_read32() + OLED_TIMEOUT; +#endif + + static const uint8_t PROGMEM display_on[] = +#ifdef OLED_FADE_OUT + {I2C_CMD, FADE_BLINK, 0x00}; +#else + {I2C_CMD, DISPLAY_ON}; +#endif + + if (!oled_active) { + if (I2C_TRANSMIT_P(display_on) != I2C_STATUS_SUCCESS) { + print("oled_on cmd failed\n"); + return oled_active; + } + oled_active = true; + } + return oled_active; +} + +bool oled_off(void) { + if (!oled_initialized) { + return !oled_active; + } + + static const uint8_t PROGMEM display_off[] = +#ifdef OLED_FADE_OUT + {I2C_CMD, FADE_BLINK, ENABLE_FADE | OLED_FADE_OUT_INTERVAL}; +#else + {I2C_CMD, DISPLAY_OFF}; +#endif + + if (oled_active) { + if (I2C_TRANSMIT_P(display_off) != I2C_STATUS_SUCCESS) { + print("oled_off cmd failed\n"); + return oled_active; + } + oled_active = false; + } + return !oled_active; +} + +bool is_oled_on(void) { + return oled_active; +} + +uint8_t oled_set_brightness(uint8_t level) { + if (!oled_initialized) { + return oled_brightness; + } + + uint8_t set_contrast[] = {I2C_CMD, CONTRAST, level}; + if (oled_brightness != level) { + if (I2C_TRANSMIT(set_contrast) != I2C_STATUS_SUCCESS) { + print("set_brightness cmd failed\n"); + return oled_brightness; + } + oled_brightness = level; + } + return oled_brightness; +} + +uint8_t oled_get_brightness(void) { + return oled_brightness; +} + +// Set the specific 8 lines rows of the screen to scroll. +// 0 is the default for start, and 7 for end, which is the entire +// height of the screen. For 128x32 screens, rows 4-7 are not used. +void oled_scroll_set_area(uint8_t start_line, uint8_t end_line) { + oled_scroll_start = start_line; + oled_scroll_end = end_line; +} + +void oled_scroll_set_speed(uint8_t speed) { + // Sets the speed for scrolling... does not take effect + // until scrolling is either started or restarted + // the ssd1306 supports 8 speeds + // FrameRate2 speed = 7 + // FrameRate3 speed = 4 + // FrameRate4 speed = 5 + // FrameRate5 speed = 0 + // FrameRate25 speed = 6 + // FrameRate64 speed = 1 + // FrameRate128 speed = 2 + // FrameRate256 speed = 3 + // for ease of use these are remaped here to be in order + static const uint8_t scroll_remap[8] = {7, 4, 5, 0, 6, 1, 2, 3}; + oled_scroll_speed = scroll_remap[speed]; +} + +bool oled_scroll_right(void) { + if (!oled_initialized) { + return oled_scrolling; + } + + // Dont enable scrolling if we need to update the display + // This prevents scrolling of bad data from starting the scroll too early after init + if (!oled_dirty && !oled_scrolling) { + uint8_t display_scroll_right[] = {I2C_CMD, SCROLL_RIGHT, 0x00, oled_scroll_start, oled_scroll_speed, oled_scroll_end, 0x00, 0xFF, ACTIVATE_SCROLL}; + if (I2C_TRANSMIT(display_scroll_right) != I2C_STATUS_SUCCESS) { + print("oled_scroll_right cmd failed\n"); + return oled_scrolling; + } + oled_scrolling = true; + } + return oled_scrolling; +} + +bool oled_scroll_left(void) { + if (!oled_initialized) { + return oled_scrolling; + } + + // Dont enable scrolling if we need to update the display + // This prevents scrolling of bad data from starting the scroll too early after init + if (!oled_dirty && !oled_scrolling) { + uint8_t display_scroll_left[] = {I2C_CMD, SCROLL_LEFT, 0x00, oled_scroll_start, oled_scroll_speed, oled_scroll_end, 0x00, 0xFF, ACTIVATE_SCROLL}; + if (I2C_TRANSMIT(display_scroll_left) != I2C_STATUS_SUCCESS) { + print("oled_scroll_left cmd failed\n"); + return oled_scrolling; + } + oled_scrolling = true; + } + return oled_scrolling; +} + +bool oled_scroll_off(void) { + if (!oled_initialized) { + return !oled_scrolling; + } + + if (oled_scrolling) { + static const uint8_t PROGMEM display_scroll_off[] = {I2C_CMD, DEACTIVATE_SCROLL}; + if (I2C_TRANSMIT_P(display_scroll_off) != I2C_STATUS_SUCCESS) { + print("oled_scroll_off cmd failed\n"); + return oled_scrolling; + } + oled_scrolling = false; + oled_dirty = OLED_ALL_BLOCKS_MASK; + } + return !oled_scrolling; +} + +bool is_oled_scrolling(void) { + return oled_scrolling; +} + +bool oled_invert(bool invert) { + if (!oled_initialized) { + return oled_inverted; + } + + if (invert && !oled_inverted) { + static const uint8_t PROGMEM display_inverted[] = {I2C_CMD, INVERT_DISPLAY}; + if (I2C_TRANSMIT_P(display_inverted) != I2C_STATUS_SUCCESS) { + print("oled_invert cmd failed\n"); + return oled_inverted; + } + oled_inverted = true; + } else if (!invert && oled_inverted) { + static const uint8_t PROGMEM display_normal[] = {I2C_CMD, NORMAL_DISPLAY}; + if (I2C_TRANSMIT_P(display_normal) != I2C_STATUS_SUCCESS) { + print("oled_invert cmd failed\n"); + return oled_inverted; + } + oled_inverted = false; + } + + return oled_inverted; +} + +uint8_t oled_max_chars(void) { + if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { + return OLED_DISPLAY_WIDTH / OLED_FONT_WIDTH; + } + return OLED_DISPLAY_HEIGHT / OLED_FONT_WIDTH; +} + +uint8_t oled_max_lines(void) { + if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { + return OLED_DISPLAY_HEIGHT / OLED_FONT_HEIGHT; + } + return OLED_DISPLAY_WIDTH / OLED_FONT_HEIGHT; +} + +void oled_task(void) { + if (!oled_initialized) { + return; + } + +#if OLED_UPDATE_INTERVAL > 0 + if (timer_elapsed(oled_update_timeout) >= OLED_UPDATE_INTERVAL) { + oled_update_timeout = timer_read(); + oled_set_cursor(0, 0); + oled_task_kb(); + } +#else + oled_set_cursor(0, 0); + oled_task_kbr(); +#endif + +#if OLED_SCROLL_TIMEOUT > 0 + if (oled_dirty && oled_scrolling) { + oled_scroll_timeout = timer_read32() + OLED_SCROLL_TIMEOUT; + oled_scroll_off(); + } +#endif + + // Smart render system, no need to check for dirty + oled_render(); + + // Display timeout check +#if OLED_TIMEOUT > 0 + if (oled_active && timer_expired32(timer_read32(), oled_timeout)) { + oled_off(); + } +#endif + +#if OLED_SCROLL_TIMEOUT > 0 + if (!oled_scrolling && timer_expired32(timer_read32(), oled_scroll_timeout)) { +# ifdef OLED_SCROLL_TIMEOUT_RIGHT + oled_scroll_right(); +# else + oled_scroll_left(); +# endif + } +#endif +} + +__attribute__((weak)) bool oled_task_kb(void) { + return oled_task_user(); +} +__attribute__((weak)) bool oled_task_user(void) { + return true; +} diff --git a/users/drashna/rules.mk b/users/drashna/rules.mk index 276a6e487e35..43186b024ae4 100644 --- a/users/drashna/rules.mk +++ b/users/drashna/rules.mk @@ -94,6 +94,12 @@ endif CUSTOM_OLED_DRIVER ?= yes ifeq ($(strip $(OLED_ENABLE)), yes) + ifeq ($(strip $(OLED_DRIVER)), custom) + OPT_DEFS += -DOLED_ENABLE \ + -DOLED_DRIVER_SH1107 + SRC += $(USER_PATH)/oled/sh110x.c + QUANTUM_LIB_SRC += i2c_master.c + endif ifeq ($(strip $(CUSTOM_OLED_DRIVER)), yes) OPT_DEFS += -DCUSTOM_OLED_DRIVER SRC += $(USER_PATH)/oled/oled_stuff.c From 0ef46d9824ccfd59d1e3aa7ccb5cfc22e428d83c Mon Sep 17 00:00:00 2001 From: Drashna Jael're Date: Sun, 30 Apr 2023 00:20:09 -0700 Subject: [PATCH 13/26] Get missed file' --- .../tractyl_manuform/5x6_right/keymaps/drashna/config.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/config.h b/keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/config.h index 217022e04900..f159a69174e8 100644 --- a/keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/config.h +++ b/keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/config.h @@ -18,9 +18,8 @@ #define DEBOUNCE 45 -#ifdef OLED_ENABLE -# define OLED_IC OLED_IC_SH1107 -# define OLED_DISPLAY_128X128 +#ifdef OLED_DRIVER_SH1107 +# undef OLED_DISPLAY_128X64 #endif #define CHARYBDIS_MINIMUM_DEFAULT_DPI 1200 From a41dad38ad4c0dbf702879cce56e21a695e2baa8 Mon Sep 17 00:00:00 2001 From: Drashna Jael're Date: Tue, 2 May 2023 10:56:34 -0700 Subject: [PATCH 14/26] Remove unneeded col offset --- drivers/oled/oled_driver.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/oled/oled_driver.h b/drivers/oled/oled_driver.h index ef69ce29d0cc..feb69b2de6a4 100644 --- a/drivers/oled/oled_driver.h +++ b/drivers/oled/oled_driver.h @@ -150,9 +150,6 @@ along with this program. If not, see . # ifndef OLED_DISPLAY_HEIGHT # define OLED_DISPLAY_HEIGHT 128 # endif -# ifndef OLED_COLUMN_OFFSET -# define OLED_COLUMN_OFFSET 32 -# endif # ifndef OLED_MATRIX_SIZE # define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH) # endif From 98ded162a03f3f775cef7c05624a791b1696005b Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Sat, 6 May 2023 07:04:38 -0700 Subject: [PATCH 15/26] Apply suggestions from code review Co-authored-by: Sergey Vlasov --- docs/feature_oled_driver.md | 1 + drivers/oled/oled_driver.c | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/docs/feature_oled_driver.md b/docs/feature_oled_driver.md index bf2d72168d79..a0ede3298600 100644 --- a/docs/feature_oled_driver.md +++ b/docs/feature_oled_driver.md @@ -203,6 +203,7 @@ These configuration options should be placed in `config.h`. Example: |---------------------------|-----------------|--------------------------------------------------------------------------------------------------------------------------| |`OLED_DC_PIN` | Required |The pin used for the DC connection of the OLED Display. | |`OLED_CS_PIN` | Required |The pin used for the CS connection of the OLED Display. | +|`OLED_RST_PIN` | *Not defined* |The pin used for the RST connection of the OLED Display (may be left undefined if the RST pin is not connected). | |`OLED_SPI_MODE` |`3` (default) |The SPI Mode for the OLED Display (not typically changed). | |`OLED_SPI_DIVISOR` |`2` (default) |The SPI Multiplier to use for the OLED Display. | diff --git a/drivers/oled/oled_driver.c b/drivers/oled/oled_driver.c index 4f7636bd36bd..381fa368c056 100644 --- a/drivers/oled/oled_driver.c +++ b/drivers/oled/oled_driver.c @@ -33,8 +33,6 @@ along with this program. If not, see . // for SH1106: https://www.velleman.eu/downloads/29/infosheets/sh1106_datasheet.pdf // for SH1107: https://www.displayfuture.com/Display/datasheet/controller/SH1107.pdf -#include "progmem.h" - // Fundamental Commands #define CONTRAST 0x81 #define DISPLAY_ALL_ON 0xA5 @@ -192,7 +190,9 @@ uint16_t oled_update_timeout; // Transmit/Write Funcs. __attribute__((weak)) bool oled_cmd(const uint8_t *data, uint16_t size) { #if defined(OLED_TRANSPORT_SPI) - spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR); + if (!spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR)) { + return false; + } // Command Mode writePinLow(OLED_DC_PIN); // Send the commands @@ -212,22 +212,23 @@ __attribute__((weak)) bool oled_cmd(const uint8_t *data, uint16_t size) { __attribute__((weak)) bool oled_cmd_P(const uint8_t *data, uint16_t size) { #if defined(__AVR__) # if defined(OLED_TRANSPORT_SPI) - spi_status_t status = spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR); + if (!spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR)) { + return false; + } + spi_status_t status = SPI_STATUS_SUCCESS; // Command Mode writePinLow(OLED_DC_PIN); // Send the commands - for (uint16_t i = 0; i < size && status >= 0; i++) { - status = spi_write(pgm_read_byte((const char *)data++))); - if (status) break; + for (uint16_t i = 1; i < size && status >= 0; i++) { + status = spi_write(pgm_read_byte((const char *)&data[i]))); } spi_stop(); - return (status == SPI_STATUS_SUCCESS); + return (status >= 0); # elif defined(OLED_TRANSPORT_I2C) i2c_status_t status = i2c_start((OLED_DISPLAY_ADDRESS << 1) | I2C_WRITE, OLED_I2C_TIMEOUT); for (uint16_t i = 0; i < size && status >= 0; i++) { status = i2c_write(pgm_read_byte((const char *)data++), OLED_I2C_TIMEOUT); - if (status) break; } i2c_stop(); @@ -242,7 +243,7 @@ __attribute__((weak)) bool oled_cmd_P(const uint8_t *data, uint16_t size) { __attribute__((weak)) bool oled_write_reg(const uint8_t *data, uint16_t size) { #if defined(OLED_TRANSPORT_SPI) spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR); - // Command Mode + // Data Mode writePinHigh(OLED_DC_PIN); // Send the commands if (spi_transmit(data, size) != SPI_STATUS_SUCCESS) { From 87623858433e55c36be804014c407517aef38d10 Mon Sep 17 00:00:00 2001 From: Drashna Jael're Date: Sat, 6 May 2023 07:10:35 -0700 Subject: [PATCH 16/26] Remove oled_pan_section --- drivers/oled/oled_driver.c | 19 ------------------- drivers/oled/oled_driver.h | 2 -- 2 files changed, 21 deletions(-) diff --git a/drivers/oled/oled_driver.c b/drivers/oled/oled_driver.c index 381fa368c056..e4eb17f76f13 100644 --- a/drivers/oled/oled_driver.c +++ b/drivers/oled/oled_driver.c @@ -665,25 +665,6 @@ void oled_pan(bool left) { oled_dirty = OLED_ALL_BLOCKS_MASK; } -void oled_pan_section(bool left, uint16_t y_start, uint16_t y_end, uint16_t x_start, uint16_t x_end) { - uint16_t i = 0; - for (uint16_t y = y_start; y < y_end; y++) { - if (left) { - for (uint16_t x = x_start; x < x_end - 1; x++) { - i = y * OLED_DISPLAY_WIDTH + x; - oled_buffer[i] = oled_buffer[i + 1]; - oled_dirty |= ((OLED_BLOCK_TYPE)1 << (i / OLED_BLOCK_SIZE)); - } - } else { - for (uint16_t x = x_end - 1; x > 0; x--) { - i = y * OLED_DISPLAY_WIDTH + x; - oled_buffer[i] = oled_buffer[i - 1]; - oled_dirty |= ((OLED_BLOCK_TYPE)1 << (i / OLED_BLOCK_SIZE)); - } - } - } -} - oled_buffer_reader_t oled_read_raw(uint16_t start_index) { if (start_index > OLED_MATRIX_SIZE) start_index = OLED_MATRIX_SIZE; oled_buffer_reader_t ret_reader; diff --git a/drivers/oled/oled_driver.h b/drivers/oled/oled_driver.h index feb69b2de6a4..e844b81466cf 100644 --- a/drivers/oled/oled_driver.h +++ b/drivers/oled/oled_driver.h @@ -380,8 +380,6 @@ void oled_write_ln(const char *data, bool invert); // Useful for moving the screen in preparation for new drawing void oled_pan(bool left); -void oled_pan_section(bool left, uint16_t y_start, uint16_t y_end, uint16_t x_start, uint16_t x_end); - // Returns a pointer to the requested start index in the buffer plus remaining // buffer length as struct oled_buffer_reader_t oled_read_raw(uint16_t start_index); From 680c41b56ec8a50e78907dd92b5d1dec9828be66 Mon Sep 17 00:00:00 2001 From: Drashna Jael're Date: Sat, 6 May 2023 07:18:53 -0700 Subject: [PATCH 17/26] Change function names and add to doc --- docs/feature_oled_driver.md | 5 +++++ drivers/oled/oled_driver.c | 8 ++++---- drivers/oled/oled_driver.h | 3 ++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/feature_oled_driver.md b/docs/feature_oled_driver.md index a0ede3298600..f2a13e99bffa 100644 --- a/docs/feature_oled_driver.md +++ b/docs/feature_oled_driver.md @@ -293,6 +293,11 @@ bool oled_init(oled_rotation_t rotation); oled_rotation_t oled_init_kb(oled_rotation_t rotation); oled_rotation_t oled_init_user(oled_rotation_t rotation); +// Send commands/data to screen +bool oled_cmd(const uint8_t *data, uint16_t size); +bool oled_cmd_P(const uint8_t *data, uint16_t size); +bool oled_data(const uint8_t *data, uint16_t size); + // Clears the display buffer, resets cursor position to 0, and sets the buffer to dirty for rendering void oled_clear(void); diff --git a/drivers/oled/oled_driver.c b/drivers/oled/oled_driver.c index e4eb17f76f13..7078c945d4ed 100644 --- a/drivers/oled/oled_driver.c +++ b/drivers/oled/oled_driver.c @@ -240,7 +240,7 @@ __attribute__((weak)) bool oled_cmd_P(const uint8_t *data, uint16_t size) { #endif } -__attribute__((weak)) bool oled_write_reg(const uint8_t *data, uint16_t size) { +__attribute__((weak)) bool oled_data(const uint8_t *data, uint16_t size) { #if defined(OLED_TRANSPORT_SPI) spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR); // Data Mode @@ -489,7 +489,7 @@ void oled_render(void) { if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { // Send render data chunk as is - if (!oled_write_reg(&oled_buffer[OLED_BLOCK_SIZE * update_start], OLED_BLOCK_SIZE)) { + if (!oled_data(&oled_buffer[OLED_BLOCK_SIZE * update_start], OLED_BLOCK_SIZE)) { print("oled_render data failed\n"); return; } @@ -506,7 +506,7 @@ void oled_render(void) { #if OLED_IC_HAS_HORIZONTAL_MODE // Send render data chunk after rotating - if (!oled_write_reg(&temp_buffer[0], OLED_BLOCK_SIZE)) { + if (!oled_data(&temp_buffer[0], OLED_BLOCK_SIZE)) { print("oled_render90 data failed\n"); return; } @@ -524,7 +524,7 @@ void oled_render(void) { } } // Send data for the page - if (!oled_write_reg(&temp_buffer[columns_in_block * i], columns_in_block)) { + if (!oled_data(&temp_buffer[columns_in_block * i], columns_in_block)) { print("oled_render90 data failed\n"); return; } diff --git a/drivers/oled/oled_driver.h b/drivers/oled/oled_driver.h index e844b81466cf..40e19758f7a4 100644 --- a/drivers/oled/oled_driver.h +++ b/drivers/oled/oled_driver.h @@ -332,9 +332,10 @@ typedef enum { // Returns true if the OLED was initialized successfully bool oled_init(oled_rotation_t rotation); +// Send commands and data to screen bool oled_cmd(const uint8_t *data, uint16_t size); bool oled_cmd_P(const uint8_t *data, uint16_t size); -bool oled_write_reg(const uint8_t *data, uint16_t size); +bool oled_data(const uint8_t *data, uint16_t size); void oled_driver_init(void); // Called at the start of oled_init, weak function overridable by the user From c8084b114e995b20fd150108c58eb11c2acdfa3a Mon Sep 17 00:00:00 2001 From: Drashna Jael're Date: Sat, 6 May 2023 12:08:17 -0700 Subject: [PATCH 18/26] Remove array size define and use util.h's version --- drivers/oled/oled_driver.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/oled/oled_driver.c b/drivers/oled/oled_driver.c index 7078c945d4ed..47b1030de2d2 100644 --- a/drivers/oled/oled_driver.c +++ b/drivers/oled/oled_driver.c @@ -136,9 +136,6 @@ along with this program. If not, see . #define I2C_CMD 0x00 #define I2C_DATA 0x40 -#undef ARRAY_SIZE -#define ARRAY_SIZE(arr) sizeof(arr) / sizeof(arr[0]) - #define HAS_FLAGS(bits, flags) ((bits & flags) == flags) // Display buffer's is the same as the OLED memory layout From d2e6ed9e02f9eda8ed5fbefdde33fbb4e6ec8981 Mon Sep 17 00:00:00 2001 From: Drashna Jael're Date: Sat, 6 May 2023 12:11:21 -0700 Subject: [PATCH 19/26] Rename send data/cmd functions --- docs/feature_oled_driver.md | 6 +++--- drivers/oled/oled_driver.c | 42 ++++++++++++++++++------------------- drivers/oled/oled_driver.h | 6 +++--- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/docs/feature_oled_driver.md b/docs/feature_oled_driver.md index f2a13e99bffa..a91361706979 100644 --- a/docs/feature_oled_driver.md +++ b/docs/feature_oled_driver.md @@ -294,9 +294,9 @@ oled_rotation_t oled_init_kb(oled_rotation_t rotation); oled_rotation_t oled_init_user(oled_rotation_t rotation); // Send commands/data to screen -bool oled_cmd(const uint8_t *data, uint16_t size); -bool oled_cmd_P(const uint8_t *data, uint16_t size); -bool oled_data(const uint8_t *data, uint16_t size); +bool oled_send_cmd(const uint8_t *data, uint16_t size); +bool oled_send_cmd_P(const uint8_t *data, uint16_t size); +bool oled_send_data(const uint8_t *data, uint16_t size); // Clears the display buffer, resets cursor position to 0, and sets the buffer to dirty for rendering void oled_clear(void); diff --git a/drivers/oled/oled_driver.c b/drivers/oled/oled_driver.c index 47b1030de2d2..834cf53c7870 100644 --- a/drivers/oled/oled_driver.c +++ b/drivers/oled/oled_driver.c @@ -185,7 +185,7 @@ uint16_t oled_update_timeout; #endif // Transmit/Write Funcs. -__attribute__((weak)) bool oled_cmd(const uint8_t *data, uint16_t size) { +__attribute__((weak)) bool oled_send_cmd(const uint8_t *data, uint16_t size) { #if defined(OLED_TRANSPORT_SPI) if (!spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR)) { return false; @@ -206,7 +206,7 @@ __attribute__((weak)) bool oled_cmd(const uint8_t *data, uint16_t size) { #endif } -__attribute__((weak)) bool oled_cmd_P(const uint8_t *data, uint16_t size) { +__attribute__((weak)) bool oled_send_cmd_P(const uint8_t *data, uint16_t size) { #if defined(__AVR__) # if defined(OLED_TRANSPORT_SPI) if (!spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR)) { @@ -233,11 +233,11 @@ __attribute__((weak)) bool oled_cmd_P(const uint8_t *data, uint16_t size) { return (status == I2C_STATUS_SUCCESS); # endif #else - return oled_cmd(data, size); + return oled_send_cmd(data, size); #endif } -__attribute__((weak)) bool oled_data(const uint8_t *data, uint16_t size) { +__attribute__((weak)) bool oled_send_data(const uint8_t *data, uint16_t size) { #if defined(OLED_TRANSPORT_SPI) spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR); // Data Mode @@ -328,7 +328,7 @@ bool oled_init(oled_rotation_t rotation) { SH1107_MEMORY_MODE_PAGE, #endif }; - if (!oled_cmd_P(display_setup1, ARRAY_SIZE(display_setup1))) { + if (!oled_send_cmd_P(display_setup1, ARRAY_SIZE(display_setup1))) { print("oled_init cmd set 1 failed\n"); return false; } @@ -337,7 +337,7 @@ bool oled_init(oled_rotation_t rotation) { static const uint8_t PROGMEM display_normal[] = { I2C_CMD, SEGMENT_REMAP_INV, COM_SCAN_DEC, DISPLAY_OFFSET, OLED_COM_PIN_OFFSET, }; - if (!oled_cmd_P(display_normal, ARRAY_SIZE(display_normal))) { + if (!oled_send_cmd_P(display_normal, ARRAY_SIZE(display_normal))) { print("oled_init cmd normal rotation failed\n"); return false; } @@ -345,14 +345,14 @@ bool oled_init(oled_rotation_t rotation) { static const uint8_t PROGMEM display_flipped[] = { I2C_CMD, SEGMENT_REMAP, COM_SCAN_INC, DISPLAY_OFFSET, (OLED_COM_PIN_COUNT - OLED_COM_PIN_OFFSET) % OLED_COM_PIN_COUNT, }; - if (!oled_cmd_P(display_flipped, ARRAY_SIZE(display_flipped))) { + if (!oled_send_cmd_P(display_flipped, ARRAY_SIZE(display_flipped))) { print("display_flipped failed\n"); return false; } } static const uint8_t PROGMEM display_setup2[] = {I2C_CMD, COM_PINS, OLED_COM_PINS, CONTRAST, OLED_BRIGHTNESS, PRE_CHARGE_PERIOD, OLED_PRE_CHARGE_PERIOD, VCOM_DETECT, OLED_VCOM_DETECT, DISPLAY_ALL_ON_RESUME, NORMAL_DISPLAY, DEACTIVATE_SCROLL, DISPLAY_ON}; - if (!oled_cmd_P(display_setup2, ARRAY_SIZE(display_setup2))) { + if (!oled_send_cmd_P(display_setup2, ARRAY_SIZE(display_setup2))) { print("display_setup2 failed\n"); return false; } @@ -479,14 +479,14 @@ void oled_render(void) { } // Send column & page position - if (!oled_cmd(display_start, ARRAY_SIZE(display_start))) { + if (!oled_send_cmd(display_start, ARRAY_SIZE(display_start))) { print("oled_render offset command failed\n"); return; } if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { // Send render data chunk as is - if (!oled_data(&oled_buffer[OLED_BLOCK_SIZE * update_start], OLED_BLOCK_SIZE)) { + if (!oled_send_data(&oled_buffer[OLED_BLOCK_SIZE * update_start], OLED_BLOCK_SIZE)) { print("oled_render data failed\n"); return; } @@ -503,7 +503,7 @@ void oled_render(void) { #if OLED_IC_HAS_HORIZONTAL_MODE // Send render data chunk after rotating - if (!oled_data(&temp_buffer[0], OLED_BLOCK_SIZE)) { + if (!oled_send_data(&temp_buffer[0], OLED_BLOCK_SIZE)) { print("oled_render90 data failed\n"); return; } @@ -515,13 +515,13 @@ void oled_render(void) { // Send column & page position for all pages except the first one if (i > 0) { display_start[1]++; - if (!oled_cmd(display_start, ARRAY_SIZE(display_start))) { + if (!oled_send_cmd(display_start, ARRAY_SIZE(display_start))) { print("oled_render offset command failed\n"); return; } } // Send data for the page - if (!oled_data(&temp_buffer[columns_in_block * i], columns_in_block)) { + if (!oled_send_data(&temp_buffer[columns_in_block * i], columns_in_block)) { print("oled_render90 data failed\n"); return; } @@ -751,7 +751,7 @@ bool oled_on(void) { #endif if (!oled_active) { - if (!oled_cmd_P(display_on, ARRAY_SIZE(display_on))) { + if (!oled_send_cmd_P(display_on, ARRAY_SIZE(display_on))) { print("oled_on cmd failed\n"); return oled_active; } @@ -773,7 +773,7 @@ bool oled_off(void) { #endif if (oled_active) { - if (!oled_cmd_P(display_off, ARRAY_SIZE(display_off))) { + if (!oled_send_cmd_P(display_off, ARRAY_SIZE(display_off))) { print("oled_off cmd failed\n"); return oled_active; } @@ -793,7 +793,7 @@ uint8_t oled_set_brightness(uint8_t level) { uint8_t set_contrast[] = {I2C_CMD, CONTRAST, level}; if (oled_brightness != level) { - if (!oled_cmd(set_contrast, ARRAY_SIZE(set_contrast))) { + if (!oled_send_cmd(set_contrast, ARRAY_SIZE(set_contrast))) { print("set_brightness cmd failed\n"); return oled_brightness; } @@ -840,7 +840,7 @@ bool oled_scroll_right(void) { // This prevents scrolling of bad data from starting the scroll too early after init if (!oled_dirty && !oled_scrolling) { uint8_t display_scroll_right[] = {I2C_CMD, SCROLL_RIGHT, 0x00, oled_scroll_start, oled_scroll_speed, oled_scroll_end, 0x00, 0xFF, ACTIVATE_SCROLL}; - if (!oled_cmd(display_scroll_right, ARRAY_SIZE(display_scroll_right))) { + if (!oled_send_cmd(display_scroll_right, ARRAY_SIZE(display_scroll_right))) { print("oled_scroll_right cmd failed\n"); return oled_scrolling; } @@ -858,7 +858,7 @@ bool oled_scroll_left(void) { // This prevents scrolling of bad data from starting the scroll too early after init if (!oled_dirty && !oled_scrolling) { uint8_t display_scroll_left[] = {I2C_CMD, SCROLL_LEFT, 0x00, oled_scroll_start, oled_scroll_speed, oled_scroll_end, 0x00, 0xFF, ACTIVATE_SCROLL}; - if (!oled_cmd(display_scroll_left, ARRAY_SIZE(display_scroll_left))) { + if (!oled_send_cmd(display_scroll_left, ARRAY_SIZE(display_scroll_left))) { print("oled_scroll_left cmd failed\n"); return oled_scrolling; } @@ -874,7 +874,7 @@ bool oled_scroll_off(void) { if (oled_scrolling) { static const uint8_t PROGMEM display_scroll_off[] = {I2C_CMD, DEACTIVATE_SCROLL}; - if (!oled_cmd_P(display_scroll_off, ARRAY_SIZE(display_scroll_off))) { + if (!oled_send_cmd_P(display_scroll_off, ARRAY_SIZE(display_scroll_off))) { print("oled_scroll_off cmd failed\n"); return oled_scrolling; } @@ -895,14 +895,14 @@ bool oled_invert(bool invert) { if (invert && !oled_inverted) { static const uint8_t PROGMEM display_inverted[] = {I2C_CMD, INVERT_DISPLAY}; - if (!oled_cmd_P(display_inverted, ARRAY_SIZE(display_inverted))) { + if (!oled_send_cmd_P(display_inverted, ARRAY_SIZE(display_inverted))) { print("oled_invert cmd failed\n"); return oled_inverted; } oled_inverted = true; } else if (!invert && oled_inverted) { static const uint8_t PROGMEM display_normal[] = {I2C_CMD, NORMAL_DISPLAY}; - if (!oled_cmd_P(display_normal, ARRAY_SIZE(display_normal))) { + if (!oled_send_cmd_P(display_normal, ARRAY_SIZE(display_normal))) { print("oled_invert cmd failed\n"); return oled_inverted; } diff --git a/drivers/oled/oled_driver.h b/drivers/oled/oled_driver.h index 40e19758f7a4..0d0a84afbe8c 100644 --- a/drivers/oled/oled_driver.h +++ b/drivers/oled/oled_driver.h @@ -333,9 +333,9 @@ typedef enum { bool oled_init(oled_rotation_t rotation); // Send commands and data to screen -bool oled_cmd(const uint8_t *data, uint16_t size); -bool oled_cmd_P(const uint8_t *data, uint16_t size); -bool oled_data(const uint8_t *data, uint16_t size); +bool oled_send_cmd(const uint8_t *data, uint16_t size); +bool oled_send_cmd_P(const uint8_t *data, uint16_t size); +bool oled_send_data(const uint8_t *data, uint16_t size); void oled_driver_init(void); // Called at the start of oled_init, weak function overridable by the user From 821b2f06cda0fcdcc52488613e09702a0ac4c79b Mon Sep 17 00:00:00 2001 From: Drashna Jael're Date: Sat, 6 May 2023 17:24:02 -0700 Subject: [PATCH 20/26] Fix compilation issue with spi and avr --- drivers/oled/oled_driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/oled/oled_driver.c b/drivers/oled/oled_driver.c index 834cf53c7870..fff6557f721f 100644 --- a/drivers/oled/oled_driver.c +++ b/drivers/oled/oled_driver.c @@ -217,7 +217,7 @@ __attribute__((weak)) bool oled_send_cmd_P(const uint8_t *data, uint16_t size) { writePinLow(OLED_DC_PIN); // Send the commands for (uint16_t i = 1; i < size && status >= 0; i++) { - status = spi_write(pgm_read_byte((const char *)&data[i]))); + status = spi_write(pgm_read_byte((const char *)&data[i])); } spi_stop(); return (status >= 0); From 88d2ae0807616933c943bc42a84ba3d5d4608c51 Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Sun, 7 May 2023 09:05:43 -0700 Subject: [PATCH 21/26] Apply suggestions from code review Co-authored-by: Sergey Vlasov --- drivers/oled/oled_driver.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/oled/oled_driver.h b/drivers/oled/oled_driver.h index 0d0a84afbe8c..6010b3036c98 100644 --- a/drivers/oled/oled_driver.h +++ b/drivers/oled/oled_driver.h @@ -88,7 +88,7 @@ along with this program. If not, see . # define OLED_BLOCK_TYPE uint8_t # endif # ifndef OLED_BLOCK_COUNT -# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) // 32 (compile time mathed) +# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) // 8 (compile time mathed) # endif # ifndef OLED_BLOCK_SIZE # define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) // 32 (compile time mathed) @@ -107,7 +107,6 @@ along with this program. If not, see . # endif #elif defined(OLED_DISPLAY_64X48) -// Double height 128x64 # ifndef OLED_DISPLAY_WIDTH # define OLED_DISPLAY_WIDTH 64 # endif @@ -143,7 +142,6 @@ along with this program. If not, see . # endif #elif defined(OLED_DISPLAY_64X128) -// Double height 128x64 # ifndef OLED_DISPLAY_WIDTH # define OLED_DISPLAY_WIDTH 64 # endif @@ -190,7 +188,7 @@ along with this program. If not, see . # define OLED_BLOCK_TYPE uint32_t # endif # ifndef OLED_BLOCK_COUNT -# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) // 64 (compile time mathed) +# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) // 32 (compile time mathed) # endif # ifndef OLED_BLOCK_SIZE # define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) // 64 (compile time mathed) From 704e8069a065d9aa68e13a75b703b69847d2d425 Mon Sep 17 00:00:00 2001 From: Drashna Jael're Date: Sun, 7 May 2023 09:09:59 -0700 Subject: [PATCH 22/26] Apply more suggestions --- drivers/oled/oled_driver.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/oled/oled_driver.h b/drivers/oled/oled_driver.h index 6010b3036c98..570158ff18ed 100644 --- a/drivers/oled/oled_driver.h +++ b/drivers/oled/oled_driver.h @@ -71,7 +71,6 @@ along with this program. If not, see . // #define OLED_TARGET_MAP { 56, 120, 48, 112, 40, 104, 32, 96, 24, 88, 16, 80, 8, 72, 0, 64 } #elif defined(OLED_DISPLAY_64X32) -// Double height 128x64 # ifndef OLED_DISPLAY_WIDTH # define OLED_DISPLAY_WIDTH 64 # endif @@ -148,6 +147,12 @@ along with this program. If not, see . # ifndef OLED_DISPLAY_HEIGHT # define OLED_DISPLAY_HEIGHT 128 # endif +# ifndef OLED_IC +# define OLED_IC OLED_IC_SH1107 +# endif +# ifndef OLED_COM_PIN_OFFSET +# define OLED_COM_PIN_OFFSET +# endif # ifndef OLED_MATRIX_SIZE # define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH) # endif @@ -181,6 +186,9 @@ along with this program. If not, see . # ifndef OLED_DISPLAY_HEIGHT # define OLED_DISPLAY_HEIGHT 128 # endif +# ifndef OLED_IC +# define OLED_IC OLED_IC_SH1107 +# endif # ifndef OLED_MATRIX_SIZE # define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH) // 2048 (compile time mathed) # endif From 60cde199b351ba67f7029f0082b62332ff68010b Mon Sep 17 00:00:00 2001 From: Drashna Jael're Date: Sun, 7 May 2023 12:14:49 -0700 Subject: [PATCH 23/26] fix missing value --- drivers/oled/oled_driver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/oled/oled_driver.h b/drivers/oled/oled_driver.h index 570158ff18ed..627a3da0ba59 100644 --- a/drivers/oled/oled_driver.h +++ b/drivers/oled/oled_driver.h @@ -151,7 +151,7 @@ along with this program. If not, see . # define OLED_IC OLED_IC_SH1107 # endif # ifndef OLED_COM_PIN_OFFSET -# define OLED_COM_PIN_OFFSET +# define OLED_COM_PIN_OFFSET 32 # endif # ifndef OLED_MATRIX_SIZE # define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH) From 5933db0e327e55be7d4dd7b1b866b3071089ac89 Mon Sep 17 00:00:00 2001 From: Drashna Jael're Date: Sun, 7 May 2023 12:23:22 -0700 Subject: [PATCH 24/26] doc rewording and additions --- docs/feature_oled_driver.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/feature_oled_driver.md b/docs/feature_oled_driver.md index a91361706979..a62294b23a62 100644 --- a/docs/feature_oled_driver.md +++ b/docs/feature_oled_driver.md @@ -209,7 +209,7 @@ These configuration options should be placed in `config.h`. Example: ## 128x64 & Custom sized OLED Displays - The default display size for this feature is 128x32 and all necessary defines are precalculated with that in mind. We have added a define, `OLED_DISPLAY_128X64`, to switch all the values to be used in a 128x64 display, as well as added a custom define, `OLED_DISPLAY_CUSTOM`, that allows you to provide the necessary values to the driver. + The default display size for this feature is 128x32, and the defaults are set with that in mind. However, there are a number of additional presets for common sizes that we have added. You can define one of these values to use the presets. If your display doesn't match one of these presets, you can define `OLED_DISPLAY_CUSTOM` to manually specify all of the values. |Define |Default |Description | |----------------------|---------------|---------------------------------------------------------------------------------------------------------------------------------------| @@ -220,6 +220,8 @@ These configuration options should be placed in `config.h`. Example: |`OLED_DISPLAY_128X128`|*Not defined* |Changes the display defines for use with 128x128 displays. | |`OLED_DISPLAY_CUSTOM` |*Not defined* |Changes the display defines for use with custom displays.
Requires user to implement the below defines. | +!> 64x128 and 128x128 displays default to the SH1107 IC type, as these heights are not supported by the other IC types. + |Define |Default |Description | | --------------------|---------------|----------------------------------------------------------------------------------------------------------------------------------------| |`OLED_DISPLAY_WIDTH` |`128` |The width of the OLED display. | @@ -236,8 +238,6 @@ These configuration options should be placed in `config.h`. Example: ### 90 Degree Rotation - Technical Mumbo Jumbo -!> Rotation is unsupported on the SH1106. - ```c // OLED Rotation enum values are flags typedef enum { From f8a49c41373b9f821b05ef791f84e4618353b092 Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Mon, 8 May 2023 10:24:33 -0700 Subject: [PATCH 25/26] fix spi start Co-authored-by: Sergey Vlasov --- drivers/oled/oled_driver.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/oled/oled_driver.c b/drivers/oled/oled_driver.c index fff6557f721f..dc7a71341df7 100644 --- a/drivers/oled/oled_driver.c +++ b/drivers/oled/oled_driver.c @@ -239,7 +239,9 @@ __attribute__((weak)) bool oled_send_cmd_P(const uint8_t *data, uint16_t size) { __attribute__((weak)) bool oled_send_data(const uint8_t *data, uint16_t size) { #if defined(OLED_TRANSPORT_SPI) - spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR); + if (!spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR)) { + return false; + } // Data Mode writePinHigh(OLED_DC_PIN); // Send the commands From a98652bf56d45dab0141811a3619c0b181773083 Mon Sep 17 00:00:00 2001 From: Drashna Jael're Date: Mon, 8 May 2023 11:09:24 -0700 Subject: [PATCH 26/26] Remove extra includes --- drivers/oled/oled_driver.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/oled/oled_driver.c b/drivers/oled/oled_driver.c index dc7a71341df7..e50a88112019 100644 --- a/drivers/oled/oled_driver.c +++ b/drivers/oled/oled_driver.c @@ -26,7 +26,6 @@ along with this program. If not, see . #include "print.h" #include #include "progmem.h" -#include "keyboard.h" #include "wait.h" // Used commands from spec sheet: https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf