qp_st7735.c (6995B)
1 // Copyright 2021 Paul Cotter (@gr1mr3aver) 2 // Copyright 2021-2023 Nick Brassel (@tzarc) 3 // Copyright 2022 David Hoelscher (@customMK) 4 // SPDX-License-Identifier: GPL-2.0-or-later 5 6 #include "qp_internal.h" 7 #include "qp_comms.h" 8 #include "qp_st7735.h" 9 #include "qp_st77xx_opcodes.h" 10 #include "qp_st7735_opcodes.h" 11 #include "qp_tft_panel.h" 12 13 #ifdef QUANTUM_PAINTER_ST7735_SPI_ENABLE 14 # include "qp_comms_spi.h" 15 #endif // QUANTUM_PAINTER_ST7735_SPI_ENABLE 16 17 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 18 // Common 19 20 // Driver storage 21 tft_panel_dc_reset_painter_device_t st7735_drivers[ST7735_NUM_DEVICES] = {0}; 22 23 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 24 // Automatic viewport offsets 25 26 #ifndef ST7735_NO_AUTOMATIC_OFFSETS 27 static inline void st7735_automatic_viewport_offsets(painter_device_t device, painter_rotation_t rotation) { 28 painter_driver_t *driver = (painter_driver_t *)device; 29 30 // clang-format off 31 const struct { 32 uint16_t offset_x; 33 uint16_t offset_y; 34 } rotation_offsets_80x160[] = { 35 [QP_ROTATION_0] = { .offset_x = 24, .offset_y = 0 }, 36 [QP_ROTATION_90] = { .offset_x = 0, .offset_y = 24 }, 37 [QP_ROTATION_180] = { .offset_x = 24, .offset_y = 0 }, 38 [QP_ROTATION_270] = { .offset_x = 0, .offset_y = 24 }, 39 }; 40 // clang-format on 41 42 if (driver->panel_width == 80 && driver->panel_height == 160) { 43 driver->offset_x = rotation_offsets_80x160[rotation].offset_x; 44 driver->offset_y = rotation_offsets_80x160[rotation].offset_y; 45 } 46 } 47 #endif // ST7735_NO_AUTOMATIC_OFFSETS 48 49 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 50 // Initialization 51 52 __attribute__((weak)) bool qp_st7735_init(painter_device_t device, painter_rotation_t rotation) { 53 // clang-format off 54 const uint8_t st7735_init_sequence[] = { 55 // Command, Delay, N, Data[N] 56 ST77XX_CMD_RESET, 120, 0, 57 ST77XX_CMD_SLEEP_OFF, 5, 0, 58 ST77XX_SET_PIX_FMT, 0, 1, 0x55, 59 ST77XX_CMD_INVERT_OFF, 0, 0, 60 ST77XX_CMD_NORMAL_ON, 0, 0, 61 ST7735_SET_PGAMMA, 0, 16, 0x02, 0x1C, 0x07, 0x12, 0x37, 0x32, 0x29, 0x2D, 0x29, 0x25, 0x2B, 0x39, 0x00, 0x01, 0x03, 0x10, 62 ST7735_SET_NGAMMA, 0, 16, 0x03, 0x1D, 0x07, 0x06, 0x2E, 0x2C, 0x29, 0x2D, 0x2E, 0x2E, 0x37, 0x3F, 0x00, 0x00, 0x02, 0x10, 63 ST77XX_CMD_DISPLAY_ON, 20, 0 64 }; 65 // clang-format on 66 if (!qp_comms_bulk_command_sequence(device, st7735_init_sequence, sizeof(st7735_init_sequence))) { 67 return false; 68 } 69 70 // Configure the rotation (i.e. the ordering and direction of memory writes in GRAM) 71 const uint8_t madctl[] = { 72 [QP_ROTATION_0] = ST77XX_MADCTL_BGR, 73 [QP_ROTATION_90] = ST77XX_MADCTL_BGR | ST77XX_MADCTL_MX | ST77XX_MADCTL_MV, 74 [QP_ROTATION_180] = ST77XX_MADCTL_BGR | ST77XX_MADCTL_MX | ST77XX_MADCTL_MY, 75 [QP_ROTATION_270] = ST77XX_MADCTL_BGR | ST77XX_MADCTL_MV | ST77XX_MADCTL_MY, 76 }; 77 if (!qp_comms_command_databyte(device, ST77XX_SET_MADCTL, madctl[rotation])) { 78 return false; 79 } 80 81 #ifndef ST7735_NO_AUTOMATIC_VIEWPORT_OFFSETS 82 st7735_automatic_viewport_offsets(device, rotation); 83 #endif // ST7735_NO_AUTOMATIC_VIEWPORT_OFFSETS 84 85 return true; 86 } 87 88 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 89 // Driver vtable 90 91 const tft_panel_dc_reset_painter_driver_vtable_t st7735_driver_vtable = { 92 .base = 93 { 94 .init = qp_st7735_init, 95 .power = qp_tft_panel_power, 96 .clear = qp_tft_panel_clear, 97 .flush = qp_tft_panel_flush, 98 .pixdata = qp_tft_panel_pixdata, 99 .viewport = qp_tft_panel_viewport, 100 .palette_convert = qp_tft_panel_palette_convert_rgb565_swapped, 101 .append_pixels = qp_tft_panel_append_pixels_rgb565, 102 .append_pixdata = qp_tft_panel_append_pixdata, 103 }, 104 .num_window_bytes = 2, 105 .swap_window_coords = false, 106 .opcodes = 107 { 108 .display_on = ST77XX_CMD_DISPLAY_ON, 109 .display_off = ST77XX_CMD_DISPLAY_OFF, 110 .set_column_address = ST77XX_SET_COL_ADDR, 111 .set_row_address = ST77XX_SET_ROW_ADDR, 112 .enable_writes = ST77XX_SET_MEM, 113 }, 114 }; 115 116 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 117 // SPI 118 119 #ifdef QUANTUM_PAINTER_ST7735_SPI_ENABLE 120 121 // Factory function for creating a handle to the ST7735 device 122 painter_device_t qp_st7735_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) { 123 for (uint32_t i = 0; i < ST7735_NUM_DEVICES; ++i) { 124 tft_panel_dc_reset_painter_device_t *driver = &st7735_drivers[i]; 125 if (!driver->base.driver_vtable) { 126 driver->base.driver_vtable = (const painter_driver_vtable_t *)&st7735_driver_vtable; 127 driver->base.comms_vtable = (const painter_comms_vtable_t *)&spi_comms_with_dc_vtable; 128 driver->base.panel_width = panel_width; 129 driver->base.panel_height = panel_height; 130 driver->base.rotation = QP_ROTATION_0; 131 driver->base.offset_x = 0; 132 driver->base.offset_y = 0; 133 driver->base.native_bits_per_pixel = 16; // RGB565 134 135 // SPI and other pin configuration 136 driver->base.comms_config = &driver->spi_dc_reset_config; 137 driver->spi_dc_reset_config.spi_config.chip_select_pin = chip_select_pin; 138 driver->spi_dc_reset_config.spi_config.divisor = spi_divisor; 139 driver->spi_dc_reset_config.spi_config.lsb_first = false; 140 driver->spi_dc_reset_config.spi_config.mode = spi_mode; 141 driver->spi_dc_reset_config.dc_pin = dc_pin; 142 driver->spi_dc_reset_config.reset_pin = reset_pin; 143 driver->spi_dc_reset_config.command_params_uses_command_pin = false; 144 145 if (!qp_internal_register_device((painter_device_t)driver)) { 146 memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t)); 147 return NULL; 148 } 149 150 return (painter_device_t)driver; 151 } 152 } 153 return NULL; 154 } 155 156 #endif // QUANTUM_PAINTER_ST7735_SPI_ENABLE 157 158 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////