qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

qp_sh1107.c (9660B)


      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     return qp_comms_bulk_command_sequence(device, sh1107_init_sequence, sizeof(sh1107_init_sequence));
     77 }
     78 
     79 // Screen flush
     80 bool qp_sh1107_flush(painter_device_t device) {
     81     sh1107_device_t *driver = (sh1107_device_t *)device;
     82 
     83     if (!driver->oled.surface.dirty.is_dirty) {
     84         return true;
     85     }
     86 
     87     switch (driver->oled.base.rotation) {
     88         default:
     89         case QP_ROTATION_0:
     90             qp_oled_panel_page_column_flush_rot0(device, &driver->oled.surface.dirty, driver->framebuffer);
     91             break;
     92         case QP_ROTATION_90:
     93             qp_oled_panel_page_column_flush_rot90(device, &driver->oled.surface.dirty, driver->framebuffer);
     94             break;
     95         case QP_ROTATION_180:
     96             qp_oled_panel_page_column_flush_rot180(device, &driver->oled.surface.dirty, driver->framebuffer);
     97             break;
     98         case QP_ROTATION_270:
     99             qp_oled_panel_page_column_flush_rot270(device, &driver->oled.surface.dirty, driver->framebuffer);
    100             break;
    101     }
    102 
    103     // Clear the dirty area
    104     qp_flush(&driver->oled.surface);
    105 
    106     return true;
    107 }
    108 
    109 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    110 // Driver vtable
    111 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    112 
    113 const oled_panel_painter_driver_vtable_t sh1107_driver_vtable = {
    114     .base =
    115         {
    116             .init            = qp_sh1107_init,
    117             .power           = qp_oled_panel_power,
    118             .clear           = qp_oled_panel_clear,
    119             .flush           = qp_sh1107_flush,
    120             .pixdata         = qp_oled_panel_passthru_pixdata,
    121             .viewport        = qp_oled_panel_passthru_viewport,
    122             .palette_convert = qp_oled_panel_passthru_palette_convert,
    123             .append_pixels   = qp_oled_panel_passthru_append_pixels,
    124             .append_pixdata  = qp_oled_panel_passthru_append_pixdata,
    125         },
    126     .opcodes =
    127         {
    128             .display_on     = SH1107_DISPLAY_ON,
    129             .display_off    = SH1107_DISPLAY_OFF,
    130             .set_page       = SH1107_PAGE_ADDR,
    131             .set_column_lsb = SH1107_SETCOLUMN_LSB,
    132             .set_column_msb = SH1107_SETCOLUMN_MSB,
    133         },
    134 };
    135 
    136 #ifdef QUANTUM_PAINTER_SH1107_SPI_ENABLE
    137 // Factory function for creating a handle to the SH1107 device
    138 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) {
    139     for (uint32_t i = 0; i < SH1107_NUM_DEVICES; ++i) {
    140         sh1107_device_t *driver = &sh1107_drivers[i];
    141         if (!driver->oled.base.driver_vtable) {
    142             painter_device_t surface = qp_make_mono1bpp_surface_advanced(&driver->oled.surface, 1, panel_width, panel_height, driver->framebuffer);
    143             if (!surface) {
    144                 return NULL;
    145             }
    146 
    147             // Setup the OLED device
    148             driver->oled.base.driver_vtable         = (const painter_driver_vtable_t *)&sh1107_driver_vtable;
    149             driver->oled.base.comms_vtable          = (const painter_comms_vtable_t *)&spi_comms_with_dc_vtable;
    150             driver->oled.base.native_bits_per_pixel = 1; // 1bpp mono
    151             driver->oled.base.panel_width           = panel_width;
    152             driver->oled.base.panel_height          = panel_height;
    153             driver->oled.base.rotation              = QP_ROTATION_0;
    154             driver->oled.base.offset_x              = 0;
    155             driver->oled.base.offset_y              = 0;
    156 
    157             // SPI and other pin configuration
    158             driver->oled.base.comms_config                                   = &driver->oled.spi_dc_reset_config;
    159             driver->oled.spi_dc_reset_config.spi_config.chip_select_pin      = chip_select_pin;
    160             driver->oled.spi_dc_reset_config.spi_config.divisor              = spi_divisor;
    161             driver->oled.spi_dc_reset_config.spi_config.lsb_first            = false;
    162             driver->oled.spi_dc_reset_config.spi_config.mode                 = spi_mode;
    163             driver->oled.spi_dc_reset_config.dc_pin                          = dc_pin;
    164             driver->oled.spi_dc_reset_config.reset_pin                       = reset_pin;
    165             driver->oled.spi_dc_reset_config.command_params_uses_command_pin = true;
    166 
    167             if (!qp_internal_register_device((painter_device_t)driver)) {
    168                 memset(driver, 0, sizeof(sh1107_device_t));
    169                 return NULL;
    170             }
    171 
    172             return (painter_device_t)driver;
    173         }
    174     }
    175     return NULL;
    176 }
    177 
    178 #endif // QUANTUM_PAINTER_SH1107_SPI_ENABLE
    179 
    180 #ifdef QUANTUM_PAINTER_SH1107_I2C_ENABLE
    181 // Factory function for creating a handle to the SH1107 device
    182 painter_device_t qp_sh1107_make_i2c_device(uint16_t panel_width, uint16_t panel_height, uint8_t i2c_address) {
    183     for (uint32_t i = 0; i < SH1107_NUM_DEVICES; ++i) {
    184         sh1107_device_t *driver = &sh1107_drivers[i];
    185         if (!driver->oled.base.driver_vtable) {
    186             // Instantiate the surface
    187             painter_device_t surface = qp_make_mono1bpp_surface_advanced(&driver->oled.surface, 1, panel_width, panel_height, driver->framebuffer);
    188             if (!surface) {
    189                 return NULL;
    190             }
    191 
    192             // Setup the OLED device
    193             driver->oled.base.driver_vtable         = (const painter_driver_vtable_t *)&sh1107_driver_vtable;
    194             driver->oled.base.comms_vtable          = (const painter_comms_vtable_t *)&i2c_comms_cmddata_vtable;
    195             driver->oled.base.native_bits_per_pixel = 1; // 1bpp mono
    196             driver->oled.base.panel_width           = panel_width;
    197             driver->oled.base.panel_height          = panel_height;
    198             driver->oled.base.rotation              = QP_ROTATION_0;
    199             driver->oled.base.offset_x              = 0;
    200             driver->oled.base.offset_y              = 0;
    201 
    202             // I2C configuration
    203             driver->oled.base.comms_config       = &driver->oled.i2c_config;
    204             driver->oled.i2c_config.chip_address = i2c_address;
    205 
    206             if (!qp_internal_register_device((painter_device_t)driver)) {
    207                 memset(driver, 0, sizeof(sh1107_device_t));
    208                 return NULL;
    209             }
    210 
    211             return (painter_device_t)driver;
    212         }
    213     }
    214     return NULL;
    215 }
    216 
    217 #endif // QUANTUM_PAINTER_SH1107_I2C_ENABLE