#include #include #include "arch/sys_arch.h" #include "driver/gpio.h" #include "driver/spi_common.h" #include "driver/spi_master.h" #include "esp_err.h" #include "hal/gpio_types.h" #include "hal/spi_types.h" #include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_chip_info.h" #include "esp_flash.h" #include "esp_system.h" #include "soc/clk_tree_defs.h" #include "liska.inc" #define LCD_CMD_NOP 0x00 // This command is empty command #define LCD_CMD_SWRESET 0x01 // Software reset registers (the built-in frame buffer is not affected) #define LCD_CMD_RDDID 0x04 // Read 24-bit display ID #define LCD_CMD_RDDST 0x09 // Read display status #define LCD_CMD_RDDPM 0x0A // Read display power mode #define LCD_CMD_RDD_MADCTL 0x0B // Read display MADCTL #define LCD_CMD_RDD_COLMOD 0x0C // Read display pixel format #define LCD_CMD_RDDIM 0x0D // Read display image mode #define LCD_CMD_RDDSM 0x0E // Read display signal mode #define LCD_CMD_RDDSR 0x0F // Read display self-diagnostic result #define LCD_CMD_SLPIN 0x10 // Go into sleep mode (DC/DC, oscillator, scanning stopped, but memory keeps content) #define LCD_CMD_SLPOUT 0x11 // Exit sleep mode #define LCD_CMD_PTLON 0x12 // Turns on partial display mode #define LCD_CMD_NORON 0x13 // Turns on normal display mode #define LCD_CMD_INVOFF 0x20 // Recover from display inversion mode #define LCD_CMD_INVON 0x21 // Go into display inversion mode #define LCD_CMD_GAMSET 0x26 // Select Gamma curve for current display #define LCD_CMD_DISPOFF 0x28 // Display off (disable frame buffer output) #define LCD_CMD_DISPON 0x29 // Display on (enable frame buffer output) #define LCD_CMD_CASET 0x2A // Set column address #define LCD_CMD_RASET 0x2B // Set row address #define LCD_CMD_RAMWR 0x2C // Write frame memory #define LCD_CMD_RAMRD 0x2E // Read frame memory #define LCD_CMD_PTLAR 0x30 // Define the partial area #define LCD_CMD_VSCRDEF 0x33 // Vertical scrolling definition #define LCD_CMD_TEOFF 0x34 // Turns off tearing effect #define LCD_CMD_TEON 0x35 // Turns on tearing effect #define LCD_CMD_MADCTL 0x36 // Memory data access control #define LCD_CMD_VSCSAD 0x37 // Vertical scroll start address #define LCD_CMD_IDMOFF 0x38 // Recover from IDLE mode #define LCD_CMD_IDMON 0x39 // Fall into IDLE mode (8 color depth is displayed) #define LCD_CMD_COLMOD 0x3A // Defines the format of RGB picture data #define LCD_CMD_RAMWRC 0x3C // Memory write continue #define LCD_CMD_RAMRDC 0x3E // Memory read continue #define LCD_CMD_STE 0x44 // Set tear scan line, tearing effect output signal when display module reaches line N #define LCD_CMD_GDCAN 0x45 // Get scan line #define LCD_CMD_WRDISBV 0x51 // Write display brightness #define LCD_CMD_RDDISBV 0x52 // Read display brightness value #define MADCTL_MH_BIT (1 << 2) // Display data latch order, 0: refresh left to right, 1: refresh right to left #define MADCTL_BGR_BIT (1 << 3) // RGB/BGR order, 0: RGB, 1: BGR #define MADCTL_ML_BIT (1 << 4) // Line address order, 0: refresh top to bottom, 1: refresh bottom to top #define MADCTL_MV_BIT (1 << 5) // Row/Column order, 0: normal mode, 1: reverse mode #define MADCTL_MX_BIT (1 << 6) // Column address order, 0: left to right, 1: right to left #define MADCTL_MY_BIT (1 << 7) // Row address order, 0: top to bottom, 1: bottom to top // Pack RGR565 values into big-endian color code. static inline uint16_t RGB(uint8_t r, uint8_t g, uint8_t b) { r &= 0x1f; g &= 0x3f; b &= 0x1f; uint16_t le = (r << 11) | (g << 5) | b; return ((le & 0xff) << 8) | (le >> 8); } static spi_device_handle_t spi_handle; struct init_cmd { uint8_t cmd; uint8_t data[16]; uint8_t data_bytes; // Length of data in above data array; 0xFF = end of cmds. }; // Manufacturer-provided magic. static const struct init_cmd init_cmds[] = { // {cmd, { data }, data_size, delay_ms} /* Power contorl B, power control = 0, DC_ENA = 1 */ {0xCF, {0x00, 0xAA, 0XE0}, 3}, /* Power on sequence control, * cp1 keeps 1 frame, 1st frame enable * vcl = 0, ddvdh=3, vgh=1, vgl=2 * DDVDH_ENH=1 */ {0xED, {0x67, 0x03, 0X12, 0X81}, 4}, /* Driver timing control A, * non-overlap=default +1 * EQ=default - 1, CR=default * pre-charge=default - 1 */ {0xE8, {0x8A, 0x01, 0x78}, 3}, /* Power control A, Vcore=1.6V, DDVDH=5.6V */ {0xCB, {0x39, 0x2C, 0x00, 0x34, 0x02}, 5}, /* Pump ratio control, DDVDH=2xVCl */ {0xF7, {0x20}, 1}, {0xF7, {0x20}, 1}, /* Driver timing control, all=0 unit */ {0xEA, {0x00, 0x00}, 2}, /* Power control 1, GVDD=4.75V */ {0xC0, {0x23}, 1}, /* Power control 2, DDVDH=VCl*2, VGH=VCl*7, VGL=-VCl*3 */ {0xC1, {0x11}, 1}, /* VCOM control 1, VCOMH=4.025V, VCOML=-0.950V */ {0xC5, {0x43, 0x4C}, 2}, /* VCOM control 2, VCOMH=VMH-2, VCOML=VML-2 */ {0xC7, {0xA0}, 1}, /* Frame rate control, f=fosc, 70Hz fps */ {0xB1, {0x00, 0x1B}, 2}, /* Enable 3G, disabled */ {0xF2, {0x00}, 1}, /* Gamma set, curve 1 */ {0x26, {0x01}, 1}, /* Positive gamma correction */ {0xE0, {0x1F, 0x36, 0x36, 0x3A, 0x0C, 0x05, 0x4F, 0X87, 0x3C, 0x08, 0x11, 0x35, 0x19, 0x13, 0x00}, 15}, /* Negative gamma correction */ {0xE1, {0x00, 0x09, 0x09, 0x05, 0x13, 0x0A, 0x30, 0x78, 0x43, 0x07, 0x0E, 0x0A, 0x26, 0x2C, 0x1F}, 15}, /* Entry mode set, Low vol detect disabled, normal display */ {0xB7, {0x07}, 1}, /* Display function control */ {0xB6, {0x08, 0x82, 0x27}, 3}, {0} }; /** * Send raw command to the LCD. * * CMD is command number (or zero if only sending data). LEN and DATA provide * data to send to the display. You can continue in the transmission using * transmit() and transmit_large(). * * Only for small data transfers if LEN and DATA is used! */ static void send_cmd(uint8_t cmd, size_t len, const uint8_t *data); /** * Send command with one-byte parameter. Useful with bitfields such as MADCTL * and COLMOD. */ static void send_cmd_bits(uint8_t cmd, uint8_t bits); /** * Send RAMWR to begin sending data to area set by begin_rect(). * * Use transfer() and transfer_large() to send pixel data in big-endian RGB565. */ static void begin_pix_data(void); /** * Send raw data chunk over SPI. */ static void transmit(size_t len, const uint8_t *data); /** * Send large data block in chunks over SPI. */ static void transmit_large(size_t len, const uint8_t *data); /** * Set area for next pixel transfer. */ static void set_rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h); /** * Set area for next pixel transfer and immediately begin one. * * Use transmit() and transmit_large() to send pixel data. */ static void begin_rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h); /** * Fill rectangle with a color. */ static void fill_rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color); /** * Draw a bitmap. (big-endian, RGB565) */ static void blit_rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint16_t *data); /* * Pin definitions. */ #define PIN_BL GPIO_NUM_2 #define PIN_DC GPIO_NUM_5 #define PIN_RST GPIO_NUM_23 #define PIN_MOSI GPIO_NUM_19 #define PIN_SCLK GPIO_NUM_18 #define PIN_CS GPIO_NUM_4 void app_main(void) { printf("Hello.\n"); // Setup GPIO. ESP_ERROR_CHECK(gpio_set_direction(PIN_BL, GPIO_MODE_OUTPUT)); ESP_ERROR_CHECK(gpio_set_direction(PIN_DC, GPIO_MODE_OUTPUT)); ESP_ERROR_CHECK(gpio_set_direction(PIN_RST, GPIO_MODE_OUTPUT)); ESP_ERROR_CHECK(gpio_set_level(PIN_RST, 0)); ESP_ERROR_CHECK(gpio_set_level(PIN_BL, 0)); // Setup SPI bus. spi_bus_config_t bus_cfg = { .mosi_io_num = PIN_MOSI, .miso_io_num = GPIO_NUM_NC, .sclk_io_num = PIN_SCLK, .max_transfer_sz = 240 * 80, .flags = 0, .quadwp_io_num = -1, .quadhd_io_num = -1, }; ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &bus_cfg, SPI_DMA_CH_AUTO)); // Setup SPI device. spi_device_interface_config_t spi_cfg = { .command_bits = 0, .address_bits = 0, .dummy_bits = 0, .mode = 0, .clock_source = SPI_CLK_SRC_DEFAULT, .duty_cycle_pos = 128, .cs_ena_pretrans = 0, .cs_ena_posttrans = 0, .clock_speed_hz = 40000000, .input_delay_ns = 0, .spics_io_num = PIN_CS, .flags = SPI_DEVICE_HALFDUPLEX, .queue_size = 1, // Increase when using pre_cb. .pre_cb = NULL, // Provide callback to set the D/C line to avoid unnecessary waiting for tx end. .post_cb = NULL, }; ESP_ERROR_CHECK(spi_bus_add_device(SPI2_HOST, &spi_cfg, &spi_handle)); printf("SPI init done.\n"); sys_delay_ms(10); // Leave the LCD in reset state for some time. ESP_ERROR_CHECK(gpio_set_level(PIN_RST, 1)); printf("Release RST.\n"); sys_delay_ms(10); // Wait for exit from reset. printf("LCD driver init...\n"); // Exit sleep mode. send_cmd(LCD_CMD_SLPOUT, 0, 0); // Tell the driver that we have 16-bit LCD. send_cmd_bits(LCD_CMD_COLMOD, 0x55); // RGB565 // Configure mirroring, swapping axes and RGB/BGR us send_cmd_bits(LCD_CMD_MADCTL, MADCTL_BGR_BIT | MADCTL_MX_BIT); // Invert colors (… to the correct color space. ☺) send_cmd(LCD_CMD_INVON, 0, 0); printf("Sending init commands..."); for (const struct init_cmd *cmd = init_cmds; cmd->cmd != 0; ++cmd) { printf(" %02x(", cmd->cmd); for (size_t pos = 0; pos < cmd->data_bytes; ++pos) { if (pos > 0) printf(","); printf("%02x", cmd->data[pos]); } printf(")"); send_cmd(cmd->cmd, cmd->data_bytes, cmd->data); } printf(" done.\n"); // Enable LCD. send_cmd(LCD_CMD_DISPON, 0, 0); // Some drawing. blit_rect(0, 0, 240, 320, liska); uint16_t colors[6] = { RGB(0xFF >> 3, 0x00 >> 2, 0x00 >> 3), RGB(0xFF >> 3, 0x7F >> 2, 0x00 >> 3), RGB(0xFF >> 3, 0xFF >> 2, 0x00 >> 3), RGB(0x00 >> 3, 0xFF >> 2, 0x00 >> 3), RGB(0x00 >> 3, 0x00 >> 2, 0xFF >> 3), RGB(0xAA >> 3, 0x00 >> 2, 0xFF >> 3), }; int x = 20; int y = 10; int h = 12; int w = 100; fill_rect(x + 4, y + 4, w, h * 6, RGB(10, 20, 10)); for (int c = 0; c < 6; ++c) { fill_rect(x, y + h * c, w, h, colors[c]); } // Enable backlight. ESP_ERROR_CHECK(gpio_set_level(PIN_BL, 1)); // Wait forever. for (;;) { sys_delay_ms(1000); } } static void transmit(size_t len, const uint8_t *data) { if (len == 0) return; spi_transaction_t trans = { .tx_buffer = data, .length = len * 8, }; ESP_ERROR_CHECK(spi_device_polling_transmit(spi_handle, &trans)); } static void transmit_large(size_t len, const uint8_t *data) { while (len > 1024) { transmit(1024, data); len -= 1024; data += 1024; } transmit(len, data); } static void send_cmd(uint8_t cmd, size_t len, const uint8_t *data) { if (cmd > 0) { uint8_t cmd_data[] = {cmd}; ESP_ERROR_CHECK(gpio_set_level(PIN_DC, 0)); sys_delay_ms(1); transmit(1, cmd_data); sys_delay_ms(1); ESP_ERROR_CHECK(gpio_set_level(PIN_DC, 1)); sys_delay_ms(1); } transmit(len, data); } static void send_cmd_bits(uint8_t cmd, uint8_t bits) { send_cmd(cmd, 1, (uint8_t[]){bits}); } static void set_rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { send_cmd(LCD_CMD_CASET, 4, (uint8_t []) { (x >> 8) & 0xFF, x & 0xFF, ((x + w - 1) >> 8) & 0xFF, (x + w - 1) & 0xFF, }); send_cmd(LCD_CMD_RASET, 4, (uint8_t []) { (y >> 8) & 0xFF, y & 0xFF, ((y + h - 1) >> 8) & 0xFF, (y + h - 1) & 0xFF, }); } static void begin_pix_data() { send_cmd(LCD_CMD_RAMWR, 0, 0); } static void begin_rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { set_rect(x, y, w, h); begin_pix_data(); } static void fill_rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) { begin_rect(x, y, w, h); uint16_t color_buf[32]; for (size_t pos = 0; pos < 32; ++pos) { color_buf[pos] = color; } size_t len = w * h * 2; while (len) { size_t this_len = len; if (this_len > 32) this_len = 32; transmit(this_len, (uint8_t *) color_buf); len -= this_len; } } static void blit_rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint16_t *data) { begin_rect(x, y, w, h); transmit_large(w * h * 2, (uint8_t *) data); }