1 /*
2 * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 // #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
8
9 #include <stdlib.h>
10 #include <sys/cdefs.h>
11 #include "freertos/FreeRTOS.h"
12 #include "freertos/task.h"
13 #include "esp_lcd_panel_interface.h"
14 #include "esp_lcd_panel_io.h"
15 #include "esp_lcd_panel_vendor.h"
16 #include "esp_lcd_panel_ops.h"
17 #include "esp_lcd_panel_commands.h"
18 #include "driver/gpio.h"
19 #include "esp_log.h"
20 #include "esp_check.h"
21
22 static const char *TAG = "lcd_panel.st7789";
23
24 static esp_err_t panel_st7789_del(esp_lcd_panel_t *panel);
25 static esp_err_t panel_st7789_reset(esp_lcd_panel_t *panel);
26 static esp_err_t panel_st7789_init(esp_lcd_panel_t *panel);
27 static esp_err_t panel_st7789_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data);
28 static esp_err_t panel_st7789_invert_color(esp_lcd_panel_t *panel, bool invert_color_data);
29 static esp_err_t panel_st7789_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
30 static esp_err_t panel_st7789_swap_xy(esp_lcd_panel_t *panel, bool swap_axes);
31 static esp_err_t panel_st7789_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap);
32 static esp_err_t panel_st7789_disp_off(esp_lcd_panel_t *panel, bool off);
33
34 typedef struct {
35 esp_lcd_panel_t base;
36 esp_lcd_panel_io_handle_t io;
37 int reset_gpio_num;
38 bool reset_level;
39 int x_gap;
40 int y_gap;
41 unsigned int bits_per_pixel;
42 uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register
43 uint8_t colmod_cal; // save surrent value of LCD_CMD_COLMOD register
44 } st7789_panel_t;
45
esp_lcd_new_panel_st7789(const esp_lcd_panel_io_handle_t io,const esp_lcd_panel_dev_config_t * panel_dev_config,esp_lcd_panel_handle_t * ret_panel)46 esp_err_t esp_lcd_new_panel_st7789(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel)
47 {
48 esp_err_t ret = ESP_OK;
49 st7789_panel_t *st7789 = NULL;
50 ESP_GOTO_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
51 st7789 = calloc(1, sizeof(st7789_panel_t));
52 ESP_GOTO_ON_FALSE(st7789, ESP_ERR_NO_MEM, err, TAG, "no mem for st7789 panel");
53
54 if (panel_dev_config->reset_gpio_num >= 0) {
55 gpio_config_t io_conf = {
56 .mode = GPIO_MODE_OUTPUT,
57 .pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num,
58 };
59 ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
60 }
61
62 switch (panel_dev_config->color_space) {
63 case ESP_LCD_COLOR_SPACE_RGB:
64 st7789->madctl_val = 0;
65 break;
66 case ESP_LCD_COLOR_SPACE_BGR:
67 st7789->madctl_val |= LCD_CMD_BGR_BIT;
68 break;
69 default:
70 ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space");
71 break;
72 }
73
74 switch (panel_dev_config->bits_per_pixel) {
75 case 16:
76 st7789->colmod_cal = 0x55;
77 break;
78 case 18:
79 st7789->colmod_cal = 0x66;
80 break;
81 default:
82 ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width");
83 break;
84 }
85
86 st7789->io = io;
87 st7789->bits_per_pixel = panel_dev_config->bits_per_pixel;
88 st7789->reset_gpio_num = panel_dev_config->reset_gpio_num;
89 st7789->reset_level = panel_dev_config->flags.reset_active_high;
90 st7789->base.del = panel_st7789_del;
91 st7789->base.reset = panel_st7789_reset;
92 st7789->base.init = panel_st7789_init;
93 st7789->base.draw_bitmap = panel_st7789_draw_bitmap;
94 st7789->base.invert_color = panel_st7789_invert_color;
95 st7789->base.set_gap = panel_st7789_set_gap;
96 st7789->base.mirror = panel_st7789_mirror;
97 st7789->base.swap_xy = panel_st7789_swap_xy;
98 st7789->base.disp_off = panel_st7789_disp_off;
99 *ret_panel = &(st7789->base);
100 ESP_LOGD(TAG, "new st7789 panel @%p", st7789);
101
102 return ESP_OK;
103
104 err:
105 if (st7789) {
106 if (panel_dev_config->reset_gpio_num >= 0) {
107 gpio_reset_pin(panel_dev_config->reset_gpio_num);
108 }
109 free(st7789);
110 }
111 return ret;
112 }
113
panel_st7789_del(esp_lcd_panel_t * panel)114 static esp_err_t panel_st7789_del(esp_lcd_panel_t *panel)
115 {
116 st7789_panel_t *st7789 = __containerof(panel, st7789_panel_t, base);
117
118 if (st7789->reset_gpio_num >= 0) {
119 gpio_reset_pin(st7789->reset_gpio_num);
120 }
121 ESP_LOGD(TAG, "del st7789 panel @%p", st7789);
122 free(st7789);
123 return ESP_OK;
124 }
125
panel_st7789_reset(esp_lcd_panel_t * panel)126 static esp_err_t panel_st7789_reset(esp_lcd_panel_t *panel)
127 {
128 st7789_panel_t *st7789 = __containerof(panel, st7789_panel_t, base);
129 esp_lcd_panel_io_handle_t io = st7789->io;
130
131 // perform hardware reset
132 if (st7789->reset_gpio_num >= 0) {
133 gpio_set_level(st7789->reset_gpio_num, st7789->reset_level);
134 vTaskDelay(pdMS_TO_TICKS(10));
135 gpio_set_level(st7789->reset_gpio_num, !st7789->reset_level);
136 vTaskDelay(pdMS_TO_TICKS(10));
137 } else { // perform software reset
138 esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0);
139 vTaskDelay(pdMS_TO_TICKS(20)); // spec, wait at least 5m before sending new command
140 }
141
142 return ESP_OK;
143 }
144
panel_st7789_init(esp_lcd_panel_t * panel)145 static esp_err_t panel_st7789_init(esp_lcd_panel_t *panel)
146 {
147 st7789_panel_t *st7789 = __containerof(panel, st7789_panel_t, base);
148 esp_lcd_panel_io_handle_t io = st7789->io;
149 // LCD goes into sleep mode and display will be turned off after power on reset, exit sleep mode first
150 esp_lcd_panel_io_tx_param(io, LCD_CMD_SLPOUT, NULL, 0);
151 vTaskDelay(pdMS_TO_TICKS(100));
152 esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
153 st7789->madctl_val,
154 }, 1);
155 esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]) {
156 st7789->colmod_cal,
157 }, 1);
158 // turn on display
159 esp_lcd_panel_io_tx_param(io, LCD_CMD_DISPON, NULL, 0);
160
161 return ESP_OK;
162 }
163
panel_st7789_draw_bitmap(esp_lcd_panel_t * panel,int x_start,int y_start,int x_end,int y_end,const void * color_data)164 static esp_err_t panel_st7789_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data)
165 {
166 st7789_panel_t *st7789 = __containerof(panel, st7789_panel_t, base);
167 assert((x_start < x_end) && (y_start < y_end) && "start position must be smaller than end position");
168 esp_lcd_panel_io_handle_t io = st7789->io;
169
170 x_start += st7789->x_gap;
171 x_end += st7789->x_gap;
172 y_start += st7789->y_gap;
173 y_end += st7789->y_gap;
174
175 // define an area of frame memory where MCU can access
176 esp_lcd_panel_io_tx_param(io, LCD_CMD_CASET, (uint8_t[]) {
177 (x_start >> 8) & 0xFF,
178 x_start & 0xFF,
179 ((x_end - 1) >> 8) & 0xFF,
180 (x_end - 1) & 0xFF,
181 }, 4);
182 esp_lcd_panel_io_tx_param(io, LCD_CMD_RASET, (uint8_t[]) {
183 (y_start >> 8) & 0xFF,
184 y_start & 0xFF,
185 ((y_end - 1) >> 8) & 0xFF,
186 (y_end - 1) & 0xFF,
187 }, 4);
188 // transfer frame buffer
189 size_t len = (x_end - x_start) * (y_end - y_start) * st7789->bits_per_pixel / 8;
190 esp_lcd_panel_io_tx_color(io, LCD_CMD_RAMWR, color_data, len);
191
192 return ESP_OK;
193 }
194
panel_st7789_invert_color(esp_lcd_panel_t * panel,bool invert_color_data)195 static esp_err_t panel_st7789_invert_color(esp_lcd_panel_t *panel, bool invert_color_data)
196 {
197 st7789_panel_t *st7789 = __containerof(panel, st7789_panel_t, base);
198 esp_lcd_panel_io_handle_t io = st7789->io;
199 int command = 0;
200 if (invert_color_data) {
201 command = LCD_CMD_INVON;
202 } else {
203 command = LCD_CMD_INVOFF;
204 }
205 esp_lcd_panel_io_tx_param(io, command, NULL, 0);
206 return ESP_OK;
207 }
208
panel_st7789_mirror(esp_lcd_panel_t * panel,bool mirror_x,bool mirror_y)209 static esp_err_t panel_st7789_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
210 {
211 st7789_panel_t *st7789 = __containerof(panel, st7789_panel_t, base);
212 esp_lcd_panel_io_handle_t io = st7789->io;
213 if (mirror_x) {
214 st7789->madctl_val |= LCD_CMD_MX_BIT;
215 } else {
216 st7789->madctl_val &= ~LCD_CMD_MX_BIT;
217 }
218 if (mirror_y) {
219 st7789->madctl_val |= LCD_CMD_MY_BIT;
220 } else {
221 st7789->madctl_val &= ~LCD_CMD_MY_BIT;
222 }
223 esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
224 st7789->madctl_val
225 }, 1);
226 return ESP_OK;
227 }
228
panel_st7789_swap_xy(esp_lcd_panel_t * panel,bool swap_axes)229 static esp_err_t panel_st7789_swap_xy(esp_lcd_panel_t *panel, bool swap_axes)
230 {
231 st7789_panel_t *st7789 = __containerof(panel, st7789_panel_t, base);
232 esp_lcd_panel_io_handle_t io = st7789->io;
233 if (swap_axes) {
234 st7789->madctl_val |= LCD_CMD_MV_BIT;
235 } else {
236 st7789->madctl_val &= ~LCD_CMD_MV_BIT;
237 }
238 esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
239 st7789->madctl_val
240 }, 1);
241 return ESP_OK;
242 }
243
panel_st7789_set_gap(esp_lcd_panel_t * panel,int x_gap,int y_gap)244 static esp_err_t panel_st7789_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap)
245 {
246 st7789_panel_t *st7789 = __containerof(panel, st7789_panel_t, base);
247 st7789->x_gap = x_gap;
248 st7789->y_gap = y_gap;
249 return ESP_OK;
250 }
251
panel_st7789_disp_off(esp_lcd_panel_t * panel,bool off)252 static esp_err_t panel_st7789_disp_off(esp_lcd_panel_t *panel, bool off)
253 {
254 st7789_panel_t *st7789 = __containerof(panel, st7789_panel_t, base);
255 esp_lcd_panel_io_handle_t io = st7789->io;
256 int command = 0;
257 if (off) {
258 command = LCD_CMD_DISPOFF;
259 } else {
260 command = LCD_CMD_DISPON;
261 }
262 esp_lcd_panel_io_tx_param(io, command, NULL, 0);
263 return ESP_OK;
264 }
265