Skip to content

Commit 5aea9a5

Browse files
committed
feat(lcd): add nt35510 LCD driver
The driver used to live in the esp-idf repo. Since v6.0, it will be mantained separately in the component registry.
1 parent e13e17e commit 5aea9a5

File tree

16 files changed

+805
-0
lines changed

16 files changed

+805
-0
lines changed

.github/workflows/upload_component.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ jobs:
8787
components/lcd/esp_lcd_ssd1681
8888
components/lcd/esp_lcd_ili9881c
8989
components/lcd/esp_lcd_lt8912b
90+
components/lcd/esp_lcd_nt35510
9091
components/io_expander/esp_io_expander
9192
components/io_expander/esp_io_expander_tca9554
9293
components/io_expander/esp_io_expander_tca95xx_16bit

components/lcd/.build-test-rules.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@ components/lcd/esp_lcd_ili9881c:
2323
- if: IDF_TARGET not in ["esp32p4"]
2424
reason: Component is supported only for esp32p4 target
2525

26+
components/lcd/esp_lcd_nt35510:
27+
depends_filepatterns:
28+
- "components/lcd/esp_lcd_nt35510/**"
29+
disable:
30+
- if: IDF_VERSION < "5.3.0"
31+
reason: Component is supported only for IDF >= 5.3
32+
- if: IDF_TARGET not in ["esp32", "esp32s2", "esp32s3"]
33+
reason: Test on these targets is sufficient
34+
2635
components/lcd/esp_lcd_ssd1681:
2736
depends_filepatterns:
2837
- "components/lcd/esp_lcd_ssd1681/**"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
idf_component_register(SRCS "esp_lcd_nt35510.c"
2+
INCLUDE_DIRS "include"
3+
REQUIRES "esp_lcd"
4+
PRIV_REQUIRES "esp_driver_gpio")
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# ESP LCD NT35510
2+
3+
[![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_nt35510/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_nt35510)
4+
5+
Implementation of the NT35510 LCD controller with esp_lcd component.
6+
7+
| LCD controller | Communication interface | Component name | Link to datasheet |
8+
| :------------: | :---------------------: | :------------: | :---------------: |
9+
| NT35510 | 8080 | esp_lcd_nt35510 | [Specification](https://www.lcdwiki.com/res/MAR4018/NT35510_Preliminary_V0.80.pdf) |
10+
11+
## Add to project
12+
13+
Packages from this repository are uploaded to [Espressif's component service](https://components.espressif.com/).
14+
You can add them to your project via `idf.py add-dependency`, e.g.
15+
16+
```bash
17+
idf.py add-dependency "espressif/esp_lcd_nt35510^1.0.0"
18+
```
19+
20+
Alternatively, you can create `idf_component.yml`. More is in [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html).
Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <stdlib.h>
8+
#include <sys/cdefs.h>
9+
#include "freertos/FreeRTOS.h"
10+
#include "freertos/task.h"
11+
#include "esp_lcd_panel_interface.h"
12+
#include "esp_lcd_panel_io.h"
13+
#include "esp_lcd_panel_vendor.h"
14+
#include "esp_lcd_panel_ops.h"
15+
#include "esp_lcd_panel_commands.h"
16+
#include "driver/gpio.h"
17+
#include "esp_log.h"
18+
#include "esp_check.h"
19+
20+
#include "esp_lcd_nt35510.h"
21+
22+
static const char *TAG = "nt35510";
23+
24+
static esp_err_t panel_nt35510_del(esp_lcd_panel_t *panel);
25+
static esp_err_t panel_nt35510_reset(esp_lcd_panel_t *panel);
26+
static esp_err_t panel_nt35510_init(esp_lcd_panel_t *panel);
27+
static esp_err_t panel_nt35510_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end,
28+
const void *color_data);
29+
static esp_err_t panel_nt35510_invert_color(esp_lcd_panel_t *panel, bool invert_color_data);
30+
static esp_err_t panel_nt35510_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
31+
static esp_err_t panel_nt35510_swap_xy(esp_lcd_panel_t *panel, bool swap_axes);
32+
static esp_err_t panel_nt35510_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap);
33+
static esp_err_t panel_nt35510_disp_on_off(esp_lcd_panel_t *panel, bool off);
34+
static esp_err_t panel_nt35510_sleep(esp_lcd_panel_t *panel, bool sleep);
35+
36+
typedef struct {
37+
esp_lcd_panel_t base;
38+
esp_lcd_panel_io_handle_t io;
39+
gpio_num_t reset_gpio_num;
40+
bool reset_level;
41+
int x_gap;
42+
int y_gap;
43+
uint8_t fb_bits_per_pixel;
44+
uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register
45+
uint8_t colmod_val; // save current value of LCD_CMD_COLMOD register
46+
} nt35510_panel_t;
47+
48+
esp_err_t
49+
esp_lcd_new_panel_nt35510(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config,
50+
esp_lcd_panel_handle_t *ret_panel)
51+
{
52+
esp_err_t ret = ESP_OK;
53+
nt35510_panel_t *nt35510 = NULL;
54+
ESP_GOTO_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
55+
nt35510 = calloc(1, sizeof(nt35510_panel_t));
56+
ESP_GOTO_ON_FALSE(nt35510, ESP_ERR_NO_MEM, err, TAG, "no mem for nt35510 panel");
57+
58+
if (panel_dev_config->reset_gpio_num >= 0) {
59+
gpio_config_t io_conf = {
60+
.mode = GPIO_MODE_OUTPUT,
61+
.pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num,
62+
};
63+
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
64+
}
65+
66+
switch (panel_dev_config->rgb_ele_order) {
67+
case LCD_RGB_ELEMENT_ORDER_RGB:
68+
nt35510->madctl_val = 0;
69+
break;
70+
case LCD_RGB_ELEMENT_ORDER_BGR:
71+
nt35510->madctl_val |= LCD_CMD_BGR_BIT;
72+
break;
73+
default:
74+
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported RGB element order");
75+
break;
76+
}
77+
78+
uint8_t fb_bits_per_pixel = 0;
79+
switch (panel_dev_config->bits_per_pixel) {
80+
case 16: // RGB565
81+
nt35510->colmod_val = 0x55;
82+
fb_bits_per_pixel = 16;
83+
break;
84+
case 18: // RGB666
85+
nt35510->colmod_val = 0x66;
86+
// each color component (R/G/B) should occupy the 6 high bits of a byte, which means 3 full bytes are required for a pixel
87+
fb_bits_per_pixel = 24;
88+
break;
89+
case 24: // RGB888
90+
nt35510->colmod_val = 0x77;
91+
fb_bits_per_pixel = 24;
92+
break;
93+
default:
94+
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width");
95+
break;
96+
}
97+
98+
nt35510->io = io;
99+
nt35510->fb_bits_per_pixel = fb_bits_per_pixel;
100+
nt35510->reset_gpio_num = panel_dev_config->reset_gpio_num;
101+
nt35510->reset_level = panel_dev_config->flags.reset_active_high;
102+
nt35510->base.del = panel_nt35510_del;
103+
nt35510->base.reset = panel_nt35510_reset;
104+
nt35510->base.init = panel_nt35510_init;
105+
nt35510->base.draw_bitmap = panel_nt35510_draw_bitmap;
106+
nt35510->base.invert_color = panel_nt35510_invert_color;
107+
nt35510->base.set_gap = panel_nt35510_set_gap;
108+
nt35510->base.mirror = panel_nt35510_mirror;
109+
nt35510->base.swap_xy = panel_nt35510_swap_xy;
110+
nt35510->base.disp_on_off = panel_nt35510_disp_on_off;
111+
nt35510->base.disp_sleep = panel_nt35510_sleep;
112+
*ret_panel = &(nt35510->base);
113+
ESP_LOGD(TAG, "new nt35510 panel @%p", nt35510);
114+
115+
return ESP_OK;
116+
117+
err:
118+
if (nt35510) {
119+
if (panel_dev_config->reset_gpio_num >= 0) {
120+
gpio_reset_pin(panel_dev_config->reset_gpio_num);
121+
}
122+
free(nt35510);
123+
}
124+
return ret;
125+
}
126+
127+
static esp_err_t panel_nt35510_del(esp_lcd_panel_t *panel)
128+
{
129+
nt35510_panel_t *nt35510 = __containerof(panel, nt35510_panel_t, base);
130+
131+
if (nt35510->reset_gpio_num >= 0) {
132+
gpio_reset_pin(nt35510->reset_gpio_num);
133+
}
134+
ESP_LOGD(TAG, "del nt35510 panel @%p", nt35510);
135+
free(nt35510);
136+
return ESP_OK;
137+
}
138+
139+
static esp_err_t panel_nt35510_reset(esp_lcd_panel_t *panel)
140+
{
141+
nt35510_panel_t *nt35510 = __containerof(panel, nt35510_panel_t, base);
142+
esp_lcd_panel_io_handle_t io = nt35510->io;
143+
144+
// perform hardware reset
145+
if (nt35510->reset_gpio_num >= 0) {
146+
gpio_set_level(nt35510->reset_gpio_num, nt35510->reset_level);
147+
vTaskDelay(pdMS_TO_TICKS(10));
148+
gpio_set_level(nt35510->reset_gpio_num, !nt35510->reset_level);
149+
vTaskDelay(pdMS_TO_TICKS(10));
150+
} else {
151+
// perform software reset
152+
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET << 8, NULL, 0), TAG,
153+
"io tx param failed");
154+
vTaskDelay(pdMS_TO_TICKS(20)); // spec, wait at least 5m before sending new command
155+
}
156+
157+
return ESP_OK;
158+
}
159+
160+
static esp_err_t panel_nt35510_init(esp_lcd_panel_t *panel)
161+
{
162+
nt35510_panel_t *nt35510 = __containerof(panel, nt35510_panel_t, base);
163+
esp_lcd_panel_io_handle_t io = nt35510->io;
164+
// LCD goes into sleep mode and display will be turned off after power on reset, exit sleep mode first
165+
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SLPOUT << 8, NULL, 0), TAG,
166+
"io tx param failed");
167+
vTaskDelay(pdMS_TO_TICKS(100));
168+
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL << 8, (uint16_t[]) {
169+
nt35510->madctl_val,
170+
}, 2), TAG, "io tx param failed");
171+
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD << 8, (uint16_t[]) {
172+
nt35510->colmod_val,
173+
}, 2), TAG, "io tx param failed");
174+
175+
return ESP_OK;
176+
}
177+
178+
static esp_err_t panel_nt35510_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end,
179+
const void *color_data)
180+
{
181+
nt35510_panel_t *nt35510 = __containerof(panel, nt35510_panel_t, base);
182+
esp_lcd_panel_io_handle_t io = nt35510->io;
183+
184+
x_start += nt35510->x_gap;
185+
x_end += nt35510->x_gap;
186+
y_start += nt35510->y_gap;
187+
y_end += nt35510->y_gap;
188+
189+
// define an area of frame memory where MCU can access
190+
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, (LCD_CMD_CASET << 8) + 0, (uint16_t[]) {
191+
(x_start >> 8) & 0xFF,
192+
}, 2), TAG, "io tx param failed");
193+
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, (LCD_CMD_CASET << 8) + 1, (uint16_t[]) {
194+
x_start & 0xFF,
195+
}, 2), TAG, "io tx param failed");
196+
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, (LCD_CMD_CASET << 8) + 2, (uint16_t[]) {
197+
((x_end - 1) >> 8) & 0xFF,
198+
}, 2), TAG, "io tx param failed");
199+
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, (LCD_CMD_CASET << 8) + 3, (uint16_t[]) {
200+
(x_end - 1) & 0xFF,
201+
}, 2), TAG, "io tx param failed");
202+
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, (LCD_CMD_RASET << 8) + 0, (uint16_t[]) {
203+
(y_start >> 8) & 0xFF,
204+
}, 2), TAG, "io tx param failed");
205+
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, (LCD_CMD_RASET << 8) + 1, (uint16_t[]) {
206+
y_start & 0xFF,
207+
}, 2), TAG, "io tx param failed");
208+
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, (LCD_CMD_RASET << 8) + 2, (uint16_t[]) {
209+
((y_end - 1) >> 8) & 0xFF,
210+
}, 2), TAG, "io tx param failed");
211+
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, (LCD_CMD_RASET << 8) + 3, (uint16_t[]) {
212+
(y_end - 1) & 0xFF,
213+
}, 2), TAG, "io tx param failed");
214+
// transfer frame buffer
215+
size_t len = (x_end - x_start) * (y_end - y_start) * nt35510->fb_bits_per_pixel / 8;
216+
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_color(io, LCD_CMD_RAMWR << 8, color_data, len), TAG, "io tx color failed");
217+
218+
return ESP_OK;
219+
}
220+
221+
static esp_err_t panel_nt35510_invert_color(esp_lcd_panel_t *panel, bool invert_color_data)
222+
{
223+
nt35510_panel_t *nt35510 = __containerof(panel, nt35510_panel_t, base);
224+
esp_lcd_panel_io_handle_t io = nt35510->io;
225+
int command = 0;
226+
if (invert_color_data) {
227+
command = LCD_CMD_INVON;
228+
} else {
229+
command = LCD_CMD_INVOFF;
230+
}
231+
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command << 8, NULL, 0), TAG,
232+
"io tx param failed");
233+
return ESP_OK;
234+
}
235+
236+
static esp_err_t panel_nt35510_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
237+
{
238+
nt35510_panel_t *nt35510 = __containerof(panel, nt35510_panel_t, base);
239+
esp_lcd_panel_io_handle_t io = nt35510->io;
240+
if (mirror_x) {
241+
nt35510->madctl_val |= LCD_CMD_MX_BIT;
242+
} else {
243+
nt35510->madctl_val &= ~LCD_CMD_MX_BIT;
244+
}
245+
if (mirror_y) {
246+
nt35510->madctl_val |= LCD_CMD_MY_BIT;
247+
} else {
248+
nt35510->madctl_val &= ~LCD_CMD_MY_BIT;
249+
}
250+
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL << 8, (uint16_t[]) {
251+
nt35510->madctl_val
252+
}, 2), TAG, "io tx param failed");
253+
return ESP_OK;
254+
}
255+
256+
static esp_err_t panel_nt35510_swap_xy(esp_lcd_panel_t *panel, bool swap_axes)
257+
{
258+
nt35510_panel_t *nt35510 = __containerof(panel, nt35510_panel_t, base);
259+
esp_lcd_panel_io_handle_t io = nt35510->io;
260+
if (swap_axes) {
261+
nt35510->madctl_val |= LCD_CMD_MV_BIT;
262+
} else {
263+
nt35510->madctl_val &= ~LCD_CMD_MV_BIT;
264+
}
265+
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL << 8, (uint16_t[]) {
266+
nt35510->madctl_val
267+
}, 2), TAG, "io tx param failed");
268+
return ESP_OK;
269+
}
270+
271+
static esp_err_t panel_nt35510_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap)
272+
{
273+
nt35510_panel_t *nt35510 = __containerof(panel, nt35510_panel_t, base);
274+
nt35510->x_gap = x_gap;
275+
nt35510->y_gap = y_gap;
276+
return ESP_OK;
277+
}
278+
279+
static esp_err_t panel_nt35510_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
280+
{
281+
nt35510_panel_t *nt35510 = __containerof(panel, nt35510_panel_t, base);
282+
esp_lcd_panel_io_handle_t io = nt35510->io;
283+
int command = 0;
284+
if (on_off) {
285+
command = LCD_CMD_DISPON;
286+
} else {
287+
command = LCD_CMD_DISPOFF;
288+
}
289+
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command << 8, NULL, 0), TAG,
290+
"io tx param failed");
291+
return ESP_OK;
292+
}
293+
294+
static esp_err_t panel_nt35510_sleep(esp_lcd_panel_t *panel, bool sleep)
295+
{
296+
nt35510_panel_t *nt35510 = __containerof(panel, nt35510_panel_t, base);
297+
esp_lcd_panel_io_handle_t io = nt35510->io;
298+
int command = 0;
299+
if (sleep) {
300+
command = LCD_CMD_SLPIN;
301+
} else {
302+
command = LCD_CMD_SLPOUT;
303+
}
304+
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command << 8, NULL, 0), TAG,
305+
"io tx param failed");
306+
vTaskDelay(pdMS_TO_TICKS(100));
307+
308+
return ESP_OK;
309+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
version: "1.0.0"
2+
description: ESP LCD NT35510
3+
url: https://github.com/espressif/esp-bsp/tree/master/components/lcd/esp_lcd_nt35510
4+
dependencies:
5+
idf: ">=5.3" # For earlier versions, NT35510 driver can be found in esp_lcd component
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
#pragma once
7+
8+
#include "esp_err.h"
9+
#include "esp_lcd_panel_dev.h"
10+
11+
#ifdef __cplusplus
12+
extern "C" {
13+
#endif
14+
15+
/**
16+
* @brief Create LCD panel for model NT35510
17+
*
18+
* @param[in] io LCD panel IO handle
19+
* @param[in] panel_dev_config general panel device configuration
20+
* @param[out] ret_panel Returned LCD panel handle
21+
* @return
22+
* - ESP_ERR_INVALID_ARG if parameter is invalid
23+
* - ESP_ERR_NO_MEM if out of memory
24+
* - ESP_OK on success
25+
*/
26+
esp_err_t esp_lcd_new_panel_nt35510(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);
27+
28+
#ifdef __cplusplus
29+
}
30+
#endif

0 commit comments

Comments
 (0)