forked from qmk/qmk_firmware
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[MERGE] Created SH1107 driver for quantum painter (24724)
- Loading branch information
Showing
6 changed files
with
334 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
#include "qp_internal.h" | ||
#include "qp_comms.h" | ||
#include "qp_surface_internal.h" | ||
#include "qp_oled_panel.h" | ||
#include "qp_sh1107.h" | ||
#include "qp_sh1107_opcodes.h" | ||
#include "qp_surface.h" | ||
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
// Driver storage | ||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
|
||
typedef struct sh1107_device_t { | ||
oled_panel_painter_device_t oled; | ||
|
||
uint8_t framebuffer[SURFACE_REQUIRED_BUFFER_BYTE_SIZE(128, 128, 1)]; | ||
} sh1107_device_t; | ||
|
||
static sh1107_device_t sh1107_drivers[SH1107_NUM_DEVICES] = {0}; | ||
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
// Quantum Painter API implementations | ||
|
||
// Initialisation | ||
__attribute__((weak)) bool qp_sh1107_init(painter_device_t device, painter_rotation_t rotation) { | ||
sh1107_device_t *driver = (sh1107_device_t *)device; | ||
|
||
// Change the surface geometry based on the panel rotation | ||
if (rotation == QP_ROTATION_90 || rotation == QP_ROTATION_270) { | ||
driver->oled.surface.base.panel_width = driver->oled.base.panel_height; | ||
driver->oled.surface.base.panel_height = driver->oled.base.panel_width; | ||
} else { | ||
driver->oled.surface.base.panel_width = driver->oled.base.panel_width; | ||
driver->oled.surface.base.panel_height = driver->oled.base.panel_height; | ||
} | ||
|
||
// Init the internal surface | ||
if (!qp_init(&driver->oled.surface.base, QP_ROTATION_0)) { | ||
qp_dprintf("Failed to init internal surface in qp_sh1107_init\n"); | ||
return false; | ||
} | ||
|
||
// clang-format off | ||
uint8_t sh1107_init_sequence[] = { | ||
// Command, Delay, N, Data[N] | ||
SH1107_SET_MUX_RATIO, 0, 1, 0x7F, // 1/128 duty | ||
SH1107_DISPLAY_OFFSET, 0, 1, 0x00, | ||
SH1107_SET_START_LINE, 0, 1, 0x00, // Different from SH1106 | ||
SH1107_SET_SEGMENT_REMAP_INV, 0, 0, | ||
SH1107_COM_SCAN_DIR_DEC, 0, 0, | ||
SH1107_COM_PADS_HW_CFG, 0, 1, 0x12, | ||
SH1107_SET_CONTRAST, 0, 1, 0x7F, | ||
SH1107_ALL_ON_RESUME, 0, 0, | ||
SH1107_NON_INVERTING_DISPLAY, 0, 0, | ||
SH1107_SET_OSC_DIVFREQ, 0, 1, 0x80, | ||
SH1107_SET_CHARGE_PUMP, 0, 1, 0x14, | ||
SH1107_DISPLAY_ON, 0, 0, | ||
}; | ||
// clang-format on | ||
|
||
// If the display height is anything other than the default 128 pixels, change SH1107_SET_MUX_RATIO data byte to the correct value | ||
if (driver->oled.base.panel_height != 128) { | ||
sh1107_init_sequence[3] = driver->oled.base.panel_height - 1; | ||
} | ||
|
||
// For smaller displays, change SH1107_COM_PADS_HW_CFG data byte from alternative (0x12) to sequential (0x02) configuration | ||
if (driver->oled.base.panel_height <= 64) { | ||
sh1107_init_sequence[20] = 0x02; | ||
} | ||
|
||
qp_comms_bulk_command_sequence(device, sh1107_init_sequence, sizeof(sh1107_init_sequence)); | ||
return true; | ||
} | ||
|
||
// Screen flush | ||
bool qp_sh1107_flush(painter_device_t device) { | ||
sh1107_device_t *driver = (sh1107_device_t *)device; | ||
|
||
if (!driver->oled.surface.dirty.is_dirty) { | ||
return true; | ||
} | ||
|
||
switch (driver->oled.base.rotation) { | ||
default: | ||
case QP_ROTATION_0: | ||
qp_oled_panel_page_column_flush_rot0(device, &driver->oled.surface.dirty, driver->framebuffer); | ||
break; | ||
case QP_ROTATION_90: | ||
qp_oled_panel_page_column_flush_rot90(device, &driver->oled.surface.dirty, driver->framebuffer); | ||
break; | ||
case QP_ROTATION_180: | ||
qp_oled_panel_page_column_flush_rot180(device, &driver->oled.surface.dirty, driver->framebuffer); | ||
break; | ||
case QP_ROTATION_270: | ||
qp_oled_panel_page_column_flush_rot270(device, &driver->oled.surface.dirty, driver->framebuffer); | ||
break; | ||
} | ||
|
||
// Clear the dirty area | ||
qp_flush(&driver->oled.surface); | ||
|
||
return true; | ||
} | ||
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
// Driver vtable | ||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
|
||
const oled_panel_painter_driver_vtable_t sh1107_driver_vtable = { | ||
.base = | ||
{ | ||
.init = qp_sh1107_init, | ||
.power = qp_oled_panel_power, | ||
.clear = qp_oled_panel_clear, | ||
.flush = qp_sh1107_flush, | ||
.pixdata = qp_oled_panel_passthru_pixdata, | ||
.viewport = qp_oled_panel_passthru_viewport, | ||
.palette_convert = qp_oled_panel_passthru_palette_convert, | ||
.append_pixels = qp_oled_panel_passthru_append_pixels, | ||
.append_pixdata = qp_oled_panel_passthru_append_pixdata, | ||
}, | ||
.opcodes = | ||
{ | ||
.display_on = SH1107_DISPLAY_ON, | ||
.display_off = SH1107_DISPLAY_OFF, | ||
.set_page = SH1107_PAGE_ADDR, | ||
.set_column_lsb = SH1107_SETCOLUMN_LSB, | ||
.set_column_msb = SH1107_SETCOLUMN_MSB, | ||
}, | ||
}; | ||
|
||
#ifdef QUANTUM_PAINTER_SH1107_SPI_ENABLE | ||
// Factory function for creating a handle to the SH1107 device | ||
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) { | ||
for (uint32_t i = 0; i < SH1107_NUM_DEVICES; ++i) { | ||
sh1107_device_t *driver = &sh1107_drivers[i]; | ||
if (!driver->oled.base.driver_vtable) { | ||
painter_device_t surface = qp_make_mono1bpp_surface_advanced(&driver->oled.surface, 1, panel_width, panel_height, driver->framebuffer); | ||
if (!surface) { | ||
return NULL; | ||
} | ||
|
||
// Setup the OLED device | ||
driver->oled.base.driver_vtable = (const painter_driver_vtable_t *)&sh1107_driver_vtable; | ||
driver->oled.base.comms_vtable = (const painter_comms_vtable_t *)&spi_comms_with_dc_vtable; | ||
driver->oled.base.native_bits_per_pixel = 1; // 1bpp mono | ||
driver->oled.base.panel_width = panel_width; | ||
driver->oled.base.panel_height = panel_height; | ||
driver->oled.base.rotation = QP_ROTATION_0; | ||
driver->oled.base.offset_x = 0; | ||
driver->oled.base.offset_y = 0; | ||
|
||
// SPI and other pin configuration | ||
driver->oled.base.comms_config = &driver->oled.spi_dc_reset_config; | ||
driver->oled.spi_dc_reset_config.spi_config.chip_select_pin = chip_select_pin; | ||
driver->oled.spi_dc_reset_config.spi_config.divisor = spi_divisor; | ||
driver->oled.spi_dc_reset_config.spi_config.lsb_first = false; | ||
driver->oled.spi_dc_reset_config.spi_config.mode = spi_mode; | ||
driver->oled.spi_dc_reset_config.dc_pin = dc_pin; | ||
driver->oled.spi_dc_reset_config.reset_pin = reset_pin; | ||
driver->oled.spi_dc_reset_config.command_params_uses_command_pin = true; | ||
|
||
if (!qp_internal_register_device((painter_device_t)driver)) { | ||
memset(driver, 0, sizeof(sh1107_device_t)); | ||
return NULL; | ||
} | ||
|
||
return (painter_device_t)driver; | ||
} | ||
} | ||
return NULL; | ||
} | ||
|
||
#endif // QUANTUM_PAINTER_SH1107_SPI_ENABLE | ||
|
||
#ifdef QUANTUM_PAINTER_SH1107_I2C_ENABLE | ||
// Factory function for creating a handle to the SH1107 device | ||
painter_device_t qp_sh1107_make_i2c_device(uint16_t panel_width, uint16_t panel_height, uint8_t i2c_address) { | ||
for (uint32_t i = 0; i < SH1107_NUM_DEVICES; ++i) { | ||
sh1107_device_t *driver = &sh1107_drivers[i]; | ||
if (!driver->oled.base.driver_vtable) { | ||
// Instantiate the surface | ||
painter_device_t surface = qp_make_mono1bpp_surface_advanced(&driver->oled.surface, 1, panel_width, panel_height, driver->framebuffer); | ||
if (!surface) { | ||
return NULL; | ||
} | ||
|
||
// Setup the OLED device | ||
driver->oled.base.driver_vtable = (const painter_driver_vtable_t *)&sh1107_driver_vtable; | ||
driver->oled.base.comms_vtable = (const painter_comms_vtable_t *)&i2c_comms_cmddata_vtable; | ||
driver->oled.base.native_bits_per_pixel = 1; // 1bpp mono | ||
driver->oled.base.panel_width = panel_width; | ||
driver->oled.base.panel_height = panel_height; | ||
driver->oled.base.rotation = QP_ROTATION_0; | ||
driver->oled.base.offset_x = 0; | ||
driver->oled.base.offset_y = 0; | ||
|
||
// I2C configuration | ||
driver->oled.base.comms_config = &driver->oled.i2c_config; | ||
driver->oled.i2c_config.chip_address = i2c_address; | ||
|
||
if (!qp_internal_register_device((painter_device_t)driver)) { | ||
memset(driver, 0, sizeof(sh1107_device_t)); | ||
return NULL; | ||
} | ||
|
||
return (painter_device_t)driver; | ||
} | ||
} | ||
return NULL; | ||
} | ||
|
||
#endif // QUANTUM_PAINTER_SH1107_I2C_ENABLE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
#pragma once | ||
|
||
#include "gpio.h" | ||
#include "qp_internal.h" | ||
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
// Quantum Painter SH1107 configurables (add to your keyboard's config.h) | ||
|
||
#if defined(QUANTUM_PAINTER_SH1107_SPI_ENABLE) && !defined(SH1107_NUM_SPI_DEVICES) | ||
/** | ||
* @def This controls the maximum number of SPI SH1107 devices that Quantum Painter can communicate with at any one time. | ||
* Increasing this number allows for multiple displays to be used. | ||
*/ | ||
# define SH1107_NUM_SPI_DEVICES 1 | ||
#else | ||
# define SH1107_NUM_SPI_DEVICES 0 | ||
#endif | ||
|
||
#if defined(QUANTUM_PAINTER_SH1107_I2C_ENABLE) && !defined(SH1107_NUM_I2C_DEVICES) | ||
/** | ||
* @def This controls the maximum number of I2C SH1107 devices that Quantum Painter can communicate with at any one time. | ||
* Increasing this number allows for multiple displays to be used. | ||
*/ | ||
# define SH1107_NUM_I2C_DEVICES 1 | ||
#else | ||
# define SH1107_NUM_I2C_DEVICES 0 | ||
#endif | ||
|
||
#define SH1107_NUM_DEVICES ((SH1107_NUM_SPI_DEVICES) + (SH1107_NUM_I2C_DEVICES)) | ||
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
// Quantum Painter SH1107 device factories | ||
|
||
#ifdef QUANTUM_PAINTER_SH1107_SPI_ENABLE | ||
|
||
/** | ||
* Factory method for an SH1107 SPI LCD device. | ||
* | ||
* @param panel_width[in] the width of the display in pixels (usually 64) | ||
* @param panel_height[in] the height of the display in pixels (usually 128) | ||
* @param chip_select_pin[in] the GPIO pin used for SPI chip select | ||
* @param dc_pin[in] the GPIO pin used for D/C control | ||
* @param reset_pin[in] the GPIO pin used for RST | ||
* @param spi_divisor[in] the SPI divisor to use when communicating with the display | ||
* @param spi_mode[in] the SPI mode to use when communicating with the display | ||
* @return the device handle used with all drawing routines in Quantum Painter | ||
*/ | ||
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); | ||
|
||
#endif // QUANTUM_PAINTER_SH1107_SPI_ENABLE | ||
|
||
#ifdef QUANTUM_PAINTER_SH1107_I2C_ENABLE | ||
|
||
/** | ||
* Factory method for an SH1107 I2C LCD device. | ||
* | ||
* @param panel_width[in] the width of the display in pixels (usually 64) | ||
* @param panel_height[in] the height of the display in pixels (usually 128) | ||
* @param i2c_address[in] the I2C address to use | ||
* @return the device handle used with all drawing routines in Quantum Painter | ||
*/ | ||
painter_device_t qp_sh1107_make_i2c_device(uint16_t panel_width, uint16_t panel_height, uint8_t i2c_address); | ||
|
||
#endif // QUANTUM_PAINTER_SH1107_I2C_ENABLE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// Copyright 2024 Steve Branam (@smbranam) | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
#pragma once | ||
|
||
#define SH1107_DISPLAY_ON 0xAF | ||
#define SH1107_DISPLAY_OFF 0xAE | ||
#define SH1107_SET_OSC_DIVFREQ 0xD5 | ||
#define SH1107_SET_MUX_RATIO 0xA8 | ||
#define SH1107_DISPLAY_OFFSET 0xD3 | ||
#define SH1107_SET_START_LINE 0xDC // Key/sole difference from SH1106 (which uses 0x40) | ||
#define SH1107_SET_CHARGE_PUMP 0x8D | ||
#define SH1107_SET_SEGMENT_REMAP_NORMAL 0xA0 | ||
#define SH1107_SET_SEGMENT_REMAP_INV 0xA1 | ||
#define SH1107_COM_SCAN_DIR_INC 0xC0 | ||
#define SH1107_COM_SCAN_DIR_DEC 0xC8 | ||
#define SH1107_COM_PADS_HW_CFG 0xDA | ||
#define SH1107_SET_CONTRAST 0x81 | ||
#define SH1107_SET_PRECHARGE_PERIOD 0xD9 | ||
#define SH1107_VCOM_DESELECT_LEVEL 0xDB | ||
#define SH1107_ALL_ON_RESUME 0xA4 | ||
#define SH1107_NON_INVERTING_DISPLAY 0xA6 | ||
#define SH1107_DEACTIVATE_SCROLL 0x2E | ||
#define SH1107_SETCOLUMN_LSB 0x00 | ||
#define SH1107_SETCOLUMN_MSB 0x10 | ||
#define SH1107_PAGE_ADDR 0xB0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters