diff options
| author | Steve Storck <steve973@gmail.com> | 2025-02-16 16:41:07 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-02-17 08:41:07 +1100 |
| commit | c00b0c5bc96b4cd5d096d3c8701e86be9aba4982 (patch) | |
| tree | a9f1adbbf6c350ee9e602b6035d5baadc8205be8 /drivers/painter | |
| parent | 164b7331c3b98165e49d38127bee366f9c545513 (diff) | |
Created SH1107 driver for quantum painter (#24724)
Co-authored-by: Sergey Vlasov <sigprof@gmail.com>
Diffstat (limited to 'drivers/painter')
| -rw-r--r-- | drivers/painter/sh1107/qp_sh1107.c | 218 | ||||
| -rw-r--r-- | drivers/painter/sh1107/qp_sh1107.h | 64 | ||||
| -rw-r--r-- | drivers/painter/sh1107/qp_sh1107_opcodes.h | 25 |
3 files changed, 307 insertions, 0 deletions
diff --git a/drivers/painter/sh1107/qp_sh1107.c b/drivers/painter/sh1107/qp_sh1107.c new file mode 100644 index 0000000000..f4cbd49e40 --- /dev/null +++ b/drivers/painter/sh1107/qp_sh1107.c | |||
| @@ -0,0 +1,218 @@ | |||
| 1 | #include "qp_internal.h" | ||
| 2 | #include "qp_comms.h" | ||
| 3 | #include "qp_surface_internal.h" | ||
| 4 | #include "qp_oled_panel.h" | ||
| 5 | #include "qp_sh1107.h" | ||
| 6 | #include "qp_sh1107_opcodes.h" | ||
| 7 | #include "qp_surface.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Driver storage | ||
| 11 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 12 | |||
| 13 | typedef struct sh1107_device_t { | ||
| 14 | oled_panel_painter_device_t oled; | ||
| 15 | |||
| 16 | uint8_t framebuffer[SURFACE_REQUIRED_BUFFER_BYTE_SIZE(128, 128, 1)]; | ||
| 17 | } sh1107_device_t; | ||
| 18 | |||
| 19 | static sh1107_device_t sh1107_drivers[SH1107_NUM_DEVICES] = {0}; | ||
| 20 | |||
| 21 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 22 | // Quantum Painter API implementations | ||
| 23 | |||
| 24 | // Initialisation | ||
| 25 | __attribute__((weak)) bool qp_sh1107_init(painter_device_t device, painter_rotation_t rotation) { | ||
| 26 | sh1107_device_t *driver = (sh1107_device_t *)device; | ||
| 27 | |||
| 28 | // Change the surface geometry based on the panel rotation | ||
| 29 | if (rotation == QP_ROTATION_90 || rotation == QP_ROTATION_270) { | ||
| 30 | driver->oled.surface.base.panel_width = driver->oled.base.panel_height; | ||
| 31 | driver->oled.surface.base.panel_height = driver->oled.base.panel_width; | ||
| 32 | } else { | ||
| 33 | driver->oled.surface.base.panel_width = driver->oled.base.panel_width; | ||
| 34 | driver->oled.surface.base.panel_height = driver->oled.base.panel_height; | ||
| 35 | } | ||
| 36 | |||
| 37 | // Init the internal surface | ||
| 38 | if (!qp_init(&driver->oled.surface.base, QP_ROTATION_0)) { | ||
| 39 | qp_dprintf("Failed to init internal surface in qp_sh1107_init\n"); | ||
| 40 | return false; | ||
| 41 | } | ||
| 42 | |||
| 43 | // clang-format off | ||
| 44 | uint8_t sh1107_init_sequence[] = { | ||
| 45 | // Command, Delay, N, Data[N] | ||
| 46 | SH1107_SET_MUX_RATIO, 0, 1, 0x7F, // 1/128 duty | ||
| 47 | SH1107_DISPLAY_OFFSET, 0, 1, 0x00, | ||
| 48 | SH1107_SET_START_LINE, 0, 1, 0x00, // Different from SH1106 | ||
| 49 | SH1107_SET_SEGMENT_REMAP_INV, 0, 0, | ||
| 50 | SH1107_COM_SCAN_DIR_DEC, 0, 0, | ||
| 51 | SH1107_COM_PADS_HW_CFG, 0, 1, 0x12, | ||
| 52 | SH1107_SET_CONTRAST, 0, 1, 0x7F, | ||
| 53 | SH1107_ALL_ON_RESUME, 0, 0, | ||
| 54 | SH1107_NON_INVERTING_DISPLAY, 0, 0, | ||
| 55 | SH1107_SET_OSC_DIVFREQ, 0, 1, 0x80, | ||
| 56 | SH1107_SET_CHARGE_PUMP, 0, 1, 0x14, | ||
| 57 | SH1107_DISPLAY_ON, 0, 0, | ||
| 58 | }; | ||
| 59 | // clang-format on | ||
| 60 | |||
| 61 | // If the display width is anything other than the default 128 pixels, change SH1107_SET_MUX_RATIO data byte to the correct value. | ||
| 62 | if (driver->oled.base.panel_width != 128) { | ||
| 63 | sh1107_init_sequence[3] = driver->oled.base.panel_width - 1; | ||
| 64 | } | ||
| 65 | |||
| 66 | // If the display width is less than the default 128 pixels, change SH1107_DISPLAY_OFFSET to use the center columns. | ||
| 67 | if (driver->oled.base.panel_width < 128) { | ||
| 68 | sh1107_init_sequence[7] = (128U - driver->oled.base.panel_width) / 2; | ||
| 69 | } | ||
| 70 | |||
| 71 | // For smaller displays, change SH1107_COM_PADS_HW_CFG data byte from alternative (0x12) to sequential (0x02) configuration | ||
| 72 | if (driver->oled.base.panel_height <= 64) { | ||
| 73 | sh1107_init_sequence[20] = 0x02; | ||
| 74 | } | ||
| 75 | |||
| 76 | qp_comms_bulk_command_sequence(device, sh1107_init_sequence, sizeof(sh1107_init_sequence)); | ||
| 77 | return true; | ||
| 78 | } | ||
| 79 | |||
| 80 | // Screen flush | ||
| 81 | bool qp_sh1107_flush(painter_device_t device) { | ||
| 82 | sh1107_device_t *driver = (sh1107_device_t *)device; | ||
| 83 | |||
| 84 | if (!driver->oled.surface.dirty.is_dirty) { | ||
| 85 | return true; | ||
| 86 | } | ||
| 87 | |||
| 88 | switch (driver->oled.base.rotation) { | ||
| 89 | default: | ||
| 90 | case QP_ROTATION_0: | ||
| 91 | qp_oled_panel_page_column_flush_rot0(device, &driver->oled.surface.dirty, driver->framebuffer); | ||
| 92 | break; | ||
| 93 | case QP_ROTATION_90: | ||
| 94 | qp_oled_panel_page_column_flush_rot90(device, &driver->oled.surface.dirty, driver->framebuffer); | ||
| 95 | break; | ||
| 96 | case QP_ROTATION_180: | ||
| 97 | qp_oled_panel_page_column_flush_rot180(device, &driver->oled.surface.dirty, driver->framebuffer); | ||
| 98 | break; | ||
| 99 | case QP_ROTATION_270: | ||
| 100 | qp_oled_panel_page_column_flush_rot270(device, &driver->oled.surface.dirty, driver->framebuffer); | ||
| 101 | break; | ||
| 102 | } | ||
| 103 | |||
| 104 | // Clear the dirty area | ||
| 105 | qp_flush(&driver->oled.surface); | ||
| 106 | |||
| 107 | return true; | ||
| 108 | } | ||
| 109 | |||
| 110 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 111 | // Driver vtable | ||
| 112 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 113 | |||
| 114 | const oled_panel_painter_driver_vtable_t sh1107_driver_vtable = { | ||
| 115 | .base = | ||
| 116 | { | ||
| 117 | .init = qp_sh1107_init, | ||
| 118 | .power = qp_oled_panel_power, | ||
| 119 | .clear = qp_oled_panel_clear, | ||
| 120 | .flush = qp_sh1107_flush, | ||
| 121 | .pixdata = qp_oled_panel_passthru_pixdata, | ||
| 122 | .viewport = qp_oled_panel_passthru_viewport, | ||
| 123 | .palette_convert = qp_oled_panel_passthru_palette_convert, | ||
| 124 | .append_pixels = qp_oled_panel_passthru_append_pixels, | ||
| 125 | .append_pixdata = qp_oled_panel_passthru_append_pixdata, | ||
| 126 | }, | ||
| 127 | .opcodes = | ||
| 128 | { | ||
| 129 | .display_on = SH1107_DISPLAY_ON, | ||
| 130 | .display_off = SH1107_DISPLAY_OFF, | ||
| 131 | .set_page = SH1107_PAGE_ADDR, | ||
| 132 | .set_column_lsb = SH1107_SETCOLUMN_LSB, | ||
| 133 | .set_column_msb = SH1107_SETCOLUMN_MSB, | ||
| 134 | }, | ||
| 135 | }; | ||
| 136 | |||
| 137 | #ifdef QUANTUM_PAINTER_SH1107_SPI_ENABLE | ||
| 138 | // Factory function for creating a handle to the SH1107 device | ||
| 139 | painter_device_t qp_sh1107_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode) { | ||
| 140 | for (uint32_t i = 0; i < SH1107_NUM_DEVICES; ++i) { | ||
| 141 | sh1107_device_t *driver = &sh1107_drivers[i]; | ||
| 142 | if (!driver->oled.base.driver_vtable) { | ||
| 143 | painter_device_t surface = qp_make_mono1bpp_surface_advanced(&driver->oled.surface, 1, panel_width, panel_height, driver->framebuffer); | ||
| 144 | if (!surface) { | ||
| 145 | return NULL; | ||
| 146 | } | ||
| 147 | |||
| 148 | // Setup the OLED device | ||
| 149 | driver->oled.base.driver_vtable = (const painter_driver_vtable_t *)&sh1107_driver_vtable; | ||
| 150 | driver->oled.base.comms_vtable = (const painter_comms_vtable_t *)&spi_comms_with_dc_vtable; | ||
| 151 | driver->oled.base.native_bits_per_pixel = 1; // 1bpp mono | ||
| 152 | driver->oled.base.panel_width = panel_width; | ||
| 153 | driver->oled.base.panel_height = panel_height; | ||
| 154 | driver->oled.base.rotation = QP_ROTATION_0; | ||
| 155 | driver->oled.base.offset_x = 0; | ||
| 156 | driver->oled.base.offset_y = 0; | ||
| 157 | |||
| 158 | // SPI and other pin configuration | ||
| 159 | driver->oled.base.comms_config = &driver->oled.spi_dc_reset_config; | ||
| 160 | driver->oled.spi_dc_reset_config.spi_config.chip_select_pin = chip_select_pin; | ||
| 161 | driver->oled.spi_dc_reset_config.spi_config.divisor = spi_divisor; | ||
| 162 | driver->oled.spi_dc_reset_config.spi_config.lsb_first = false; | ||
| 163 | driver->oled.spi_dc_reset_config.spi_config.mode = spi_mode; | ||
| 164 | driver->oled.spi_dc_reset_config.dc_pin = dc_pin; | ||
| 165 | driver->oled.spi_dc_reset_config.reset_pin = reset_pin; | ||
| 166 | driver->oled.spi_dc_reset_config.command_params_uses_command_pin = true; | ||
| 167 | |||
| 168 | if (!qp_internal_register_device((painter_device_t)driver)) { | ||
| 169 | memset(driver, 0, sizeof(sh1107_device_t)); | ||
| 170 | return NULL; | ||
| 171 | } | ||
| 172 | |||
| 173 | return (painter_device_t)driver; | ||
| 174 | } | ||
| 175 | } | ||
| 176 | return NULL; | ||
| 177 | } | ||
| 178 | |||
| 179 | #endif // QUANTUM_PAINTER_SH1107_SPI_ENABLE | ||
| 180 | |||
| 181 | #ifdef QUANTUM_PAINTER_SH1107_I2C_ENABLE | ||
| 182 | // Factory function for creating a handle to the SH1107 device | ||
| 183 | painter_device_t qp_sh1107_make_i2c_device(uint16_t panel_width, uint16_t panel_height, uint8_t i2c_address) { | ||
| 184 | for (uint32_t i = 0; i < SH1107_NUM_DEVICES; ++i) { | ||
| 185 | sh1107_device_t *driver = &sh1107_drivers[i]; | ||
| 186 | if (!driver->oled.base.driver_vtable) { | ||
| 187 | // Instantiate the surface | ||
| 188 | painter_device_t surface = qp_make_mono1bpp_surface_advanced(&driver->oled.surface, 1, panel_width, panel_height, driver->framebuffer); | ||
| 189 | if (!surface) { | ||
| 190 | return NULL; | ||
| 191 | } | ||
| 192 | |||
| 193 | // Setup the OLED device | ||
| 194 | driver->oled.base.driver_vtable = (const painter_driver_vtable_t *)&sh1107_driver_vtable; | ||
| 195 | driver->oled.base.comms_vtable = (const painter_comms_vtable_t *)&i2c_comms_cmddata_vtable; | ||
| 196 | driver->oled.base.native_bits_per_pixel = 1; // 1bpp mono | ||
| 197 | driver->oled.base.panel_width = panel_width; | ||
| 198 | driver->oled.base.panel_height = panel_height; | ||
| 199 | driver->oled.base.rotation = QP_ROTATION_0; | ||
| 200 | driver->oled.base.offset_x = 0; | ||
| 201 | driver->oled.base.offset_y = 0; | ||
| 202 | |||
| 203 | // I2C configuration | ||
| 204 | driver->oled.base.comms_config = &driver->oled.i2c_config; | ||
| 205 | driver->oled.i2c_config.chip_address = i2c_address; | ||
| 206 | |||
| 207 | if (!qp_internal_register_device((painter_device_t)driver)) { | ||
| 208 | memset(driver, 0, sizeof(sh1107_device_t)); | ||
| 209 | return NULL; | ||
| 210 | } | ||
| 211 | |||
| 212 | return (painter_device_t)driver; | ||
| 213 | } | ||
| 214 | } | ||
| 215 | return NULL; | ||
| 216 | } | ||
| 217 | |||
| 218 | #endif // QUANTUM_PAINTER_SH1107_I2C_ENABLE | ||
diff --git a/drivers/painter/sh1107/qp_sh1107.h b/drivers/painter/sh1107/qp_sh1107.h new file mode 100644 index 0000000000..2b866d7dc8 --- /dev/null +++ b/drivers/painter/sh1107/qp_sh1107.h | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "gpio.h" | ||
| 4 | #include "qp_internal.h" | ||
| 5 | |||
| 6 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 7 | // Quantum Painter SH1107 configurables (add to your keyboard's config.h) | ||
| 8 | |||
| 9 | #if defined(QUANTUM_PAINTER_SH1107_SPI_ENABLE) && !defined(SH1107_NUM_SPI_DEVICES) | ||
| 10 | /** | ||
| 11 | * @def This controls the maximum number of SPI SH1107 devices that Quantum Painter can communicate with at any one time. | ||
| 12 | * Increasing this number allows for multiple displays to be used. | ||
| 13 | */ | ||
| 14 | # define SH1107_NUM_SPI_DEVICES 1 | ||
| 15 | #else | ||
| 16 | # define SH1107_NUM_SPI_DEVICES 0 | ||
| 17 | #endif | ||
| 18 | |||
| 19 | #if defined(QUANTUM_PAINTER_SH1107_I2C_ENABLE) && !defined(SH1107_NUM_I2C_DEVICES) | ||
| 20 | /** | ||
| 21 | * @def This controls the maximum number of I2C SH1107 devices that Quantum Painter can communicate with at any one time. | ||
| 22 | * Increasing this number allows for multiple displays to be used. | ||
| 23 | */ | ||
| 24 | # define SH1107_NUM_I2C_DEVICES 1 | ||
| 25 | #else | ||
| 26 | # define SH1107_NUM_I2C_DEVICES 0 | ||
| 27 | #endif | ||
| 28 | |||
| 29 | #define SH1107_NUM_DEVICES ((SH1107_NUM_SPI_DEVICES) + (SH1107_NUM_I2C_DEVICES)) | ||
| 30 | |||
| 31 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 32 | // Quantum Painter SH1107 device factories | ||
| 33 | |||
| 34 | #ifdef QUANTUM_PAINTER_SH1107_SPI_ENABLE | ||
| 35 | |||
| 36 | /** | ||
| 37 | * Factory method for an SH1107 SPI LCD device. | ||
| 38 | * | ||
| 39 | * @param panel_width[in] the width of the display in pixels (usually 64) | ||
| 40 | * @param panel_height[in] the height of the display in pixels (usually 128) | ||
| 41 | * @param chip_select_pin[in] the GPIO pin used for SPI chip select | ||
| 42 | * @param dc_pin[in] the GPIO pin used for D/C control | ||
| 43 | * @param reset_pin[in] the GPIO pin used for RST | ||
| 44 | * @param spi_divisor[in] the SPI divisor to use when communicating with the display | ||
| 45 | * @param spi_mode[in] the SPI mode to use when communicating with the display | ||
| 46 | * @return the device handle used with all drawing routines in Quantum Painter | ||
| 47 | */ | ||
| 48 | painter_device_t qp_sh1107_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode); | ||
| 49 | |||
| 50 | #endif // QUANTUM_PAINTER_SH1107_SPI_ENABLE | ||
| 51 | |||
| 52 | #ifdef QUANTUM_PAINTER_SH1107_I2C_ENABLE | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Factory method for an SH1107 I2C LCD device. | ||
| 56 | * | ||
| 57 | * @param panel_width[in] the width of the display in pixels (usually 64) | ||
| 58 | * @param panel_height[in] the height of the display in pixels (usually 128) | ||
| 59 | * @param i2c_address[in] the I2C address to use | ||
| 60 | * @return the device handle used with all drawing routines in Quantum Painter | ||
| 61 | */ | ||
| 62 | painter_device_t qp_sh1107_make_i2c_device(uint16_t panel_width, uint16_t panel_height, uint8_t i2c_address); | ||
| 63 | |||
| 64 | #endif // QUANTUM_PAINTER_SH1107_I2C_ENABLE | ||
diff --git a/drivers/painter/sh1107/qp_sh1107_opcodes.h b/drivers/painter/sh1107/qp_sh1107_opcodes.h new file mode 100644 index 0000000000..818bf40928 --- /dev/null +++ b/drivers/painter/sh1107/qp_sh1107_opcodes.h | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | // Copyright 2024 Steve Branam (@smbranam) | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | #pragma once | ||
| 4 | |||
| 5 | #define SH1107_DISPLAY_ON 0xAF | ||
| 6 | #define SH1107_DISPLAY_OFF 0xAE | ||
| 7 | #define SH1107_SET_OSC_DIVFREQ 0xD5 | ||
| 8 | #define SH1107_SET_MUX_RATIO 0xA8 | ||
| 9 | #define SH1107_DISPLAY_OFFSET 0xD3 | ||
| 10 | #define SH1107_SET_START_LINE 0xDC // Key/sole difference from SH1106 (which uses 0x40) | ||
| 11 | #define SH1107_SET_CHARGE_PUMP 0x8D | ||
| 12 | #define SH1107_SET_SEGMENT_REMAP_NORMAL 0xA0 | ||
| 13 | #define SH1107_SET_SEGMENT_REMAP_INV 0xA1 | ||
| 14 | #define SH1107_COM_SCAN_DIR_INC 0xC0 | ||
| 15 | #define SH1107_COM_SCAN_DIR_DEC 0xC8 | ||
| 16 | #define SH1107_COM_PADS_HW_CFG 0xDA | ||
| 17 | #define SH1107_SET_CONTRAST 0x81 | ||
| 18 | #define SH1107_SET_PRECHARGE_PERIOD 0xD9 | ||
| 19 | #define SH1107_VCOM_DESELECT_LEVEL 0xDB | ||
| 20 | #define SH1107_ALL_ON_RESUME 0xA4 | ||
| 21 | #define SH1107_NON_INVERTING_DISPLAY 0xA6 | ||
| 22 | #define SH1107_DEACTIVATE_SCROLL 0x2E | ||
| 23 | #define SH1107_SETCOLUMN_LSB 0x00 | ||
| 24 | #define SH1107_SETCOLUMN_MSB 0x10 | ||
| 25 | #define SH1107_PAGE_ADDR 0xB0 | ||
