/** * @file sh1107.c * */ /********************* * INCLUDES *********************/ #include "sh1107.h" #include "disp_spi.h" #include "driver/gpio.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" /********************* * DEFINES *********************/ #define TAG "SH1107" /********************** * TYPEDEFS **********************/ /*The LCD needs a bunch of command/argument values to be initialized. They are stored in this struct. */ typedef struct { uint8_t cmd; uint8_t data[16]; uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds. } lcd_init_cmd_t; /********************** * STATIC PROTOTYPES **********************/ static void sh1107_send_cmd(uint8_t cmd); static void sh1107_send_data(void * data, uint16_t length); static void sh1107_send_color(void * data, uint16_t length); /********************** * STATIC VARIABLES **********************/ /********************** * MACROS **********************/ #define BIT_SET(a,b) ((a) |= (1U<<(b))) #define BIT_CLEAR(a,b) ((a) &= ~(1U<<(b))) /********************** * GLOBAL FUNCTIONS **********************/ void sh1107_init(void) { // Use Double Bytes Commands if necessary, but not Command+Data // Initialization taken from https://github.com/nopnop2002/esp-idf-m5stick lcd_init_cmd_t init_cmds[]={ {0xAE, {0}, 0}, // Turn display off {0xDC, {0}, 0}, // Set display start line {0x00, {0}, 0}, // ...value {0x81, {0}, 0}, // Set display contrast {0x2F, {0}, 0}, // ...value {0x20, {0}, 0}, // Set memory mode {0xA0, {0}, 0}, // Non-rotated display #if defined CONFIG_LV_DISPLAY_ORIENTATION_LANDSCAPE {0xC8, {0}, 0}, // flipped vertical #elif defined CONFIG_LV_DISPLAY_ORIENTATION_PORTRAIT {0xC7, {0}, 0}, // flipped vertical #endif {0xA8, {0}, 0}, // Set multiplex ratio {0x7F, {0}, 0}, // ...value {0xD3, {0}, 0}, // Set display offset to zero {0x60, {0}, 0}, // ...value {0xD5, {0}, 0}, // Set display clock divider {0x51, {0}, 0}, // ...value {0xD9, {0}, 0}, // Set pre-charge {0x22, {0}, 0}, // ...value {0xDB, {0}, 0}, // Set com detect {0x35, {0}, 0}, // ...value {0xB0, {0}, 0}, // Set page address {0xDA, {0}, 0}, // Set com pins {0x12, {0}, 0}, // ...value {0xA4, {0}, 0}, // output ram to display #if defined CONFIG_LV_INVERT_DISPLAY {0xA7, {0}, 0}, // inverted display #else {0xA6, {0}, 0}, // Non-inverted display #endif {0xAF, {0}, 0}, // Turn display on {0, {0}, 0xff}, }; //Initialize non-SPI GPIOs gpio_pad_select_gpio(SH1107_DC); gpio_set_direction(SH1107_DC, GPIO_MODE_OUTPUT); gpio_pad_select_gpio(SH1107_RST); gpio_set_direction(SH1107_RST, GPIO_MODE_OUTPUT); //Reset the display gpio_set_level(SH1107_RST, 0); vTaskDelay(100 / portTICK_RATE_MS); gpio_set_level(SH1107_RST, 1); vTaskDelay(100 / portTICK_RATE_MS); //Send all the commands uint16_t cmd = 0; while (init_cmds[cmd].databytes!=0xff) { sh1107_send_cmd(init_cmds[cmd].cmd); sh1107_send_data(init_cmds[cmd].data, init_cmds[cmd].databytes&0x1F); if (init_cmds[cmd].databytes & 0x80) { vTaskDelay(100 / portTICK_RATE_MS); } cmd++; } } void sh1107_set_px_cb(struct _disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa) { /* buf_w will be ignored, the configured CONFIG_LV_DISPLAY_HEIGHT and _WIDTH, and CONFIG_LV_DISPLAY_ORIENTATION_LANDSCAPE and _PORTRAIT will be used. */ uint16_t byte_index = 0; uint8_t bit_index = 0; #if defined CONFIG_LV_DISPLAY_ORIENTATION_LANDSCAPE byte_index = y + (( x>>3 ) * CONFIG_LV_DISPLAY_HEIGHT); bit_index = x & 0x7; #elif defined CONFIG_LV_DISPLAY_ORIENTATION_PORTRAIT byte_index = x + (( y>>3 ) * CONFIG_LV_DISPLAY_WIDTH); bit_index = y & 0x7; #endif if ((color.full == 0) && (LV_OPA_TRANSP != opa)) { BIT_SET(buf[byte_index], bit_index); } else { BIT_CLEAR(buf[byte_index], bit_index); } } void sh1107_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map) { uint8_t columnLow = area->x1 & 0x0F; uint8_t columnHigh = (area->x1 >> 4) & 0x0F; uint8_t row1 = 0, row2 = 0; uint32_t size = 0; void *ptr; #if defined CONFIG_LV_DISPLAY_ORIENTATION_LANDSCAPE row1 = area->x1>>3; row2 = area->x2>>3; #else row1 = area->y1>>3; row2 = area->y2>>3; #endif for(int i = row1; i < row2+1; i++){ sh1107_send_cmd(0x10 | columnHigh); // Set Higher Column Start Address for Page Addressing Mode sh1107_send_cmd(0x00 | columnLow); // Set Lower Column Start Address for Page Addressing Mode sh1107_send_cmd(0xB0 | i); // Set Page Start Address for Page Addressing Mode size = area->y2 - area->y1 + 1; #if defined CONFIG_LV_DISPLAY_ORIENTATION_LANDSCAPE ptr = color_map + i * CONFIG_LV_DISPLAY_HEIGHT; #else ptr = color_map + i * CONFIG_LV_DISPLAY_WIDTH; #endif if(i != row2){ sh1107_send_data( (void *) ptr, size); } else { // complete sending data by sh1107_send_color() and thus call lv_flush_ready() sh1107_send_color( (void *) ptr, size); } } } void sh1107_rounder(struct _disp_drv_t * disp_drv, lv_area_t *area) { // workaround: always send complete size display buffer area->x1 = 0; area->y1 = 0; area->x2 = CONFIG_LV_DISPLAY_WIDTH-1; area->y2 = CONFIG_LV_DISPLAY_HEIGHT-1; } void sh1107_sleep_in() { sh1107_send_cmd(0xAE); } void sh1107_sleep_out() { sh1107_send_cmd(0xAF); } /********************** * STATIC FUNCTIONS **********************/ static void sh1107_send_cmd(uint8_t cmd) { disp_wait_for_pending_transactions(); gpio_set_level(SH1107_DC, 0); /*Command mode*/ disp_spi_send_data(&cmd, 1); } static void sh1107_send_data(void * data, uint16_t length) { disp_wait_for_pending_transactions(); gpio_set_level(SH1107_DC, 1); /*Data mode*/ disp_spi_send_data(data, length); } static void sh1107_send_color(void * data, uint16_t length) { disp_wait_for_pending_transactions(); gpio_set_level(SH1107_DC, 1); /*Data mode*/ disp_spi_send_colors(data, length); }