Skip to content

Commit ed9cacf

Browse files
committed
Add DotClockFramebuffer
1 parent 17015b4 commit ed9cacf

File tree

20 files changed

+849
-8
lines changed

20 files changed

+849
-8
lines changed

locale/circuitpython.pot

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1224,6 +1224,7 @@ msgid "Invalid %q"
12241224
msgstr ""
12251225

12261226
#: ports/atmel-samd/common-hal/microcontroller/Pin.c
1227+
#: ports/espressif/common-hal/dotclockframebuffer/DotClockFramebuffer.c
12271228
#: ports/mimxrt10xx/common-hal/microcontroller/Pin.c
12281229
#: shared-bindings/microcontroller/Pin.c
12291230
msgid "Invalid %q pin"
@@ -1378,6 +1379,10 @@ msgstr ""
13781379
msgid "Must be a %q subclass."
13791380
msgstr ""
13801381

1382+
#: ports/espressif/common-hal/dotclockframebuffer/DotClockFramebuffer.c
1383+
msgid "Must provide 5/6/5 RGB pins"
1384+
msgstr ""
1385+
13811386
#: ports/mimxrt10xx/common-hal/busio/SPI.c shared-bindings/busio/SPI.c
13821387
msgid "Must provide MISO or MOSI pin"
13831388
msgstr ""
@@ -2948,7 +2953,7 @@ msgstr ""
29482953
msgid "empty file"
29492954
msgstr ""
29502955

2951-
#: extmod/moduasyncio.c extmod/moduheapq.c extmod/modutimeq.c
2956+
#: extmod/moduasyncio.c extmod/moduheapq.c
29522957
msgid "empty heap"
29532958
msgstr ""
29542959

@@ -3899,10 +3904,6 @@ msgstr ""
38993904
msgid "pull masks conflict with direction masks"
39003905
msgstr ""
39013906

3902-
#: extmod/modutimeq.c
3903-
msgid "queue overflow"
3904-
msgstr ""
3905-
39063907
#: py/parse.c
39073908
msgid "raw f-strings are not supported"
39083909
msgstr ""

ports/espressif/CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ if("${CIRCUITPY_ESPCAMERA}")
1212
message("Including esp32-camera")
1313
set(EXTRA_COMPONENT_DIRS "esp32-camera")
1414
list(APPEND COMPONENTS "esp32-camera")
15-
message("COMPONENTS = ${COMPONENTS}")
15+
endif()
16+
17+
if("${CIRCUITPY_DOTCLOCKFRAMEBUFFER}")
18+
message("Including esp32-camera")
19+
list(APPEND COMPONENTS "esp_lcd")
1620
endif()
1721

1822
include($ENV{IDF_PATH}/tools/cmake/project.cmake)

ports/espressif/Makefile

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,11 @@ ifneq ($(CIRCUITPY_BLEIO),0)
257257
SRC_C += common-hal/_bleio/ble_events.c
258258
endif
259259

260+
ifneq ($(CIRCUITPY_DOTCLOCKFRAMEBUFFER),0)
261+
CFLAGS += -isystem esp-idf/components/esp_lcd/include
262+
CFLAGS += -isystem esp-idf/components/esp_lcd/interface
263+
endif
264+
260265
ifneq ($(CIRCUITPY_ESPCAMERA),0)
261266
SRC_CAMERA := \
262267
$(wildcard common-hal/espcamera/*.c) \
@@ -355,7 +360,7 @@ endif
355360
do-sdkconfig: $(BUILD)/esp-idf/config/sdkconfig.h
356361
QSTR_GLOBAL_REQUIREMENTS += $(BUILD)/esp-idf/config/sdkconfig.h
357362
$(BUILD)/esp-idf/config/sdkconfig.h: boards/$(BOARD)/sdkconfig CMakeLists.txt | $(BUILD)/esp-idf
358-
IDF_PATH=$(IDF_PATH) cmake -S . -B $(BUILD)/esp-idf -DSDKCONFIG=$(BUILD)/esp-idf/sdkconfig -DSDKCONFIG_DEFAULTS="$(SDKCONFIGS)" -DCMAKE_TOOLCHAIN_FILE=$(IDF_PATH)/tools/cmake/toolchain-$(IDF_TARGET).cmake -DIDF_TARGET=$(IDF_TARGET) -GNinja -DCIRCUITPY_ESPCAMERA=$(CIRCUITPY_ESPCAMERA)
363+
IDF_PATH=$(IDF_PATH) cmake -S . -B $(BUILD)/esp-idf -DSDKCONFIG=$(BUILD)/esp-idf/sdkconfig -DSDKCONFIG_DEFAULTS="$(SDKCONFIGS)" -DCMAKE_TOOLCHAIN_FILE=$(IDF_PATH)/tools/cmake/toolchain-$(IDF_TARGET).cmake -DIDF_TARGET=$(IDF_TARGET) -GNinja -DCIRCUITPY_ESPCAMERA=$(CIRCUITPY_ESPCAMERA) -DCIRCUITPY_DOTCLOCKFRAMEBUFFER=$(CIRCUITPY_DOTCLOCKFRAMEBUFFER)
359364

360365
# build a lib
361366
# Adding -d explain -j 1 -v to the ninja line will output debug info
@@ -393,6 +398,9 @@ endif
393398
ifneq ($(CIRCUITPY_ESPULP),0)
394399
ESP_IDF_COMPONENTS_LINK += ulp
395400
endif
401+
ifneq ($(CIRCUITPY_DOTCLOCKFRAMEBUFFER),0)
402+
ESP_IDF_COMPONENTS_LINK += esp_lcd
403+
endif
396404

397405
ESP_IDF_COMPONENTS_EXPANDED = $(foreach component, $(ESP_IDF_COMPONENTS_LINK), $(BUILD)/esp-idf/esp-idf/$(component)/lib$(component).a)
398406

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
USB_VID = 0x303A
2+
USB_PID = 0x7003
3+
USB_PRODUCT = "ESP32-S3-EV-LCD-Board"
4+
USB_MANUFACTURER = "Espressif"
5+
6+
IDF_TARGET = esp32s3
7+
8+
CIRCUITPY_ESP_FLASH_MODE = dio
9+
CIRCUITPY_ESP_FLASH_FREQ = 80m
10+
CIRCUITPY_ESP_FLASH_SIZE = 16MB
11+
12+
CIRCUITPY_DOTCLOCKFRAMEBUFFER = 1
13+
UF2_BOOTLOADER = 0
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2023 Jeff Epler for Adafruit Industries
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#include <stdint.h>
28+
29+
#include "esp_intr_alloc.h"
30+
#include "esp_lcd_panel_interface.h"
31+
#include "esp_lcd_panel_rgb.h"
32+
#include "esp_pm.h"
33+
#include "esp_private/gdma.h"
34+
#include "hal/dma_types.h"
35+
#include "hal/lcd_hal.h"
36+
#include "hal/lcd_ll.h"
37+
#include "soc/lcd_periph.h"
38+
39+
// extract from esp-idf esp_lcd_rgb_panel.c
40+
typedef struct
41+
{
42+
esp_lcd_panel_t base; // Base class of generic lcd panel
43+
int panel_id; // LCD panel ID
44+
lcd_hal_context_t hal; // Hal layer object
45+
size_t data_width; // Number of data lines (e.g. for RGB565, the data width is 16)
46+
size_t sram_trans_align; // Alignment for framebuffer that allocated in SRAM
47+
size_t psram_trans_align; // Alignment for framebuffer that allocated in PSRAM
48+
int disp_gpio_num; // Display control GPIO, which is used to perform action like "disp_off"
49+
intr_handle_t intr; // LCD peripheral interrupt handle
50+
esp_pm_lock_handle_t pm_lock; // Power management lock
51+
size_t num_dma_nodes; // Number of DMA descriptors that used to carry the frame buffer
52+
uint8_t *fb; // Frame buffer
53+
size_t fb_size; // Size of frame buffer
54+
int data_gpio_nums[SOC_LCD_RGB_DATA_WIDTH]; // GPIOs used for data lines, we keep these GPIOs for action like "invert_color"
55+
size_t resolution_hz; // Peripheral clock resolution
56+
esp_lcd_rgb_timing_t timings; // RGB timing parameters (e.g. pclk, sync pulse, porch width)
57+
gdma_channel_handle_t dma_chan; // DMA channel handle
58+
esp_lcd_rgb_panel_frame_trans_done_cb_t on_frame_trans_done; // Callback, invoked after frame trans done
59+
void *user_ctx; // Reserved user's data of callback functions
60+
int x_gap; // Extra gap in x coordinate, it's used when calculate the flush window
61+
int y_gap; // Extra gap in y coordinate, it's used when calculate the flush window
62+
struct
63+
{
64+
unsigned int disp_en_level : 1; // The level which can turn on the screen by `disp_gpio_num`
65+
unsigned int stream_mode : 1; // If set, the LCD transfers data continuously, otherwise, it stops refreshing the LCD when transaction done
66+
unsigned int fb_in_psram : 1; // Whether the frame buffer is in PSRAM
67+
} flags;
68+
dma_descriptor_t dma_nodes[]; // DMA descriptor pool of size `num_dma_nodes`
69+
} esp_rgb_panel_t;
70+
71+
72+
#include "esp_log.h"
73+
#define TAG "LCD"
74+
75+
#include "components/esp_rom/include/esp_rom_sys.h"
76+
77+
78+
#include "py/objarray.h"
79+
#include "shared-bindings/dotclockframebuffer/DotClockFramebuffer.h"
80+
#include "common-hal/dotclockframebuffer/DotClockFramebuffer.h"
81+
#include "bindings/espidf/__init__.h"
82+
#include "shared-bindings/microcontroller/Pin.h"
83+
#include "py/runtime.h"
84+
#include "components/driver/include/driver/gpio.h"
85+
#include "components/driver/include/driver/periph_ctrl.h"
86+
#include "components/driver/include/esp_private/gdma.h"
87+
#include "components/esp_rom/include/esp_rom_gpio.h"
88+
#include "components/hal/esp32s3/include/hal/lcd_ll.h"
89+
#include "components/hal/include/hal/gpio_hal.h"
90+
#include "components/soc/esp32s3/include/soc/lcd_cam_struct.h"
91+
#include "esp_heap_caps.h"
92+
93+
// should be from rom/cache.h but it wasn't working
94+
int Cache_WriteBack_Addr(uint32_t addr, uint32_t size);
95+
96+
#define LCD_RGB_ISR_IRAM_SAFE (1)
97+
#define LCD_RGB_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_INTRDISABLED)
98+
99+
#define common_hal_mcu_pin_number_maybe(x) ((x) ? common_hal_mcu_pin_number((x)) : -1)
100+
101+
static void claim_and_record(const mcu_pin_obj_t *pin, uint64_t *used_pins_mask) {
102+
if (pin) {
103+
int number = common_hal_mcu_pin_number(pin);
104+
*used_pins_mask |= (UINT64_C(1) << number);
105+
claim_pin_number(number);
106+
never_reset_pin_number(number);
107+
}
108+
}
109+
110+
static int valid_pin(const mcu_pin_obj_t *pin, qstr name) {
111+
int result = common_hal_mcu_pin_number(pin);
112+
if (result == NO_PIN) {
113+
mp_raise_ValueError_varg(translate("Invalid %q pin"), name);
114+
}
115+
return result;
116+
}
117+
118+
void common_hal_dotclockframebuffer_framebuffer_construct(dotclockframebuffer_framebuffer_obj_t *self,
119+
const mcu_pin_obj_t *de,
120+
const mcu_pin_obj_t *vsync,
121+
const mcu_pin_obj_t *hsync,
122+
const mcu_pin_obj_t *dclk,
123+
const mcu_pin_obj_t **red, uint8_t num_red,
124+
const mcu_pin_obj_t **green, uint8_t num_green,
125+
const mcu_pin_obj_t **blue, uint8_t num_blue,
126+
int frequency, int width, int height,
127+
int hsync_pulse_width, int hsync_back_porch, int hsync_front_porch, bool hsync_idle_low,
128+
int vsync_pulse_width, int vsync_back_porch, int vsync_front_porch, bool vsync_idle_low,
129+
bool de_idle_high, bool pclk_active_high, bool pclk_idle_high) {
130+
131+
if (num_red != 5 || num_green != 6 || num_blue != 5) {
132+
mp_raise_ValueError(translate("Must provide 5/6/5 RGB pins"));
133+
}
134+
135+
claim_and_record(de, &self->used_pins_mask);
136+
claim_and_record(vsync, &self->used_pins_mask);
137+
claim_and_record(hsync, &self->used_pins_mask);
138+
claim_and_record(dclk, &self->used_pins_mask);
139+
140+
for (size_t i = 0; i < num_red; i++) {
141+
claim_and_record(red[i], &self->used_pins_mask);
142+
}
143+
for (size_t i = 0; i < num_green; i++) {
144+
claim_and_record(green[i], &self->used_pins_mask);
145+
}
146+
for (size_t i = 0; i < num_blue; i++) {
147+
claim_and_record(blue[i], &self->used_pins_mask);
148+
}
149+
150+
esp_lcd_rgb_panel_config_t *cfg = &self->panel_config;
151+
cfg->timings.pclk_hz = frequency;
152+
cfg->timings.h_res = width;
153+
cfg->timings.v_res = height;
154+
cfg->timings.hsync_pulse_width = hsync_pulse_width;
155+
cfg->timings.hsync_back_porch = hsync_back_porch;
156+
cfg->timings.hsync_front_porch = hsync_front_porch;
157+
cfg->timings.vsync_pulse_width = vsync_pulse_width;
158+
cfg->timings.vsync_back_porch = vsync_back_porch;
159+
cfg->timings.vsync_front_porch = vsync_front_porch;
160+
cfg->timings.flags.hsync_idle_low = hsync_idle_low;
161+
cfg->timings.flags.vsync_idle_low = hsync_idle_low;
162+
cfg->timings.flags.de_idle_high = de_idle_high;
163+
cfg->timings.flags.pclk_active_neg = !pclk_active_high;
164+
cfg->timings.flags.pclk_idle_high = pclk_idle_high;
165+
166+
cfg->data_width = 16;
167+
cfg->sram_trans_align = 8;
168+
cfg->psram_trans_align = 64;
169+
cfg->hsync_gpio_num = valid_pin(hsync, MP_QSTR_hsync);
170+
cfg->vsync_gpio_num = valid_pin(vsync, MP_QSTR_vsync);
171+
cfg->de_gpio_num = valid_pin(de, MP_QSTR_de);
172+
cfg->pclk_gpio_num = valid_pin(dclk, MP_QSTR_dclk);
173+
174+
cfg->data_gpio_nums[0] = valid_pin(blue[0], MP_QSTR_blue);
175+
cfg->data_gpio_nums[1] = valid_pin(blue[1], MP_QSTR_blue);
176+
cfg->data_gpio_nums[2] = valid_pin(blue[2], MP_QSTR_blue);
177+
cfg->data_gpio_nums[3] = valid_pin(blue[3], MP_QSTR_blue);
178+
cfg->data_gpio_nums[4] = valid_pin(blue[4], MP_QSTR_blue);
179+
180+
cfg->data_gpio_nums[5] = valid_pin(green[0], MP_QSTR_green);
181+
cfg->data_gpio_nums[6] = valid_pin(green[1], MP_QSTR_green);
182+
cfg->data_gpio_nums[7] = valid_pin(green[2], MP_QSTR_green);
183+
cfg->data_gpio_nums[8] = valid_pin(green[3], MP_QSTR_green);
184+
cfg->data_gpio_nums[9] = valid_pin(green[4], MP_QSTR_green);
185+
cfg->data_gpio_nums[10] = valid_pin(green[5], MP_QSTR_green);
186+
187+
cfg->data_gpio_nums[11] = valid_pin(red[0], MP_QSTR_red);
188+
cfg->data_gpio_nums[12] = valid_pin(red[1], MP_QSTR_red);
189+
cfg->data_gpio_nums[13] = valid_pin(red[2], MP_QSTR_red);
190+
cfg->data_gpio_nums[14] = valid_pin(red[3], MP_QSTR_red);
191+
cfg->data_gpio_nums[15] = valid_pin(red[4], MP_QSTR_red);
192+
193+
cfg->disp_gpio_num = GPIO_NUM_NC;
194+
195+
cfg->flags.disp_active_low = 0;
196+
cfg->flags.relax_on_idle = 0;
197+
cfg->flags.fb_in_psram = 1; // allocate frame buffer in PSRAM
198+
199+
ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&self->panel_config, &self->panel_handle));
200+
ESP_ERROR_CHECK(esp_lcd_panel_reset(self->panel_handle));
201+
ESP_ERROR_CHECK(esp_lcd_panel_init(self->panel_handle));
202+
203+
uint16_t color = 0;
204+
ESP_ERROR_CHECK(self->panel_handle->draw_bitmap(self->panel_handle, 0, 0, 1, 1, &color));
205+
206+
esp_rgb_panel_t *_rgb_panel = __containerof(self->panel_handle, esp_rgb_panel_t, base);
207+
208+
self->frequency = frequency;
209+
self->refresh_rate = frequency / (width + hsync_front_porch + hsync_back_porch) / (height + vsync_front_porch + vsync_back_porch);
210+
self->bufinfo.buf = _rgb_panel->fb;
211+
self->bufinfo.len = 2 * width * height;
212+
self->bufinfo.typecode = 'H' | MP_OBJ_ARRAY_TYPECODE_FLAG_RW;
213+
214+
memset(self->bufinfo.buf, 0xaa, width * height);
215+
memset(self->bufinfo.buf + width * height, 0x55, width * height);
216+
217+
// LCD_CAM.lcd_ctrl2.lcd_vsync_idle_pol = _vsync_polarity;
218+
// LCD_CAM.lcd_ctrl2.lcd_hsync_idle_pol = _hsync_polarity;
219+
220+
}
221+
222+
223+
void common_hal_dotclockframebuffer_framebuffer_deinit(dotclockframebuffer_framebuffer_obj_t *self) {
224+
if (common_hal_dotclockframebuffer_framebuffer_deinitialized(self)) {
225+
return;
226+
}
227+
228+
reset_pin_mask(self->used_pins_mask);
229+
self->used_pins_mask = 0;
230+
esp_lcd_panel_del(self->panel_handle);
231+
}
232+
233+
bool common_hal_dotclockframebuffer_framebuffer_deinitialized(dotclockframebuffer_framebuffer_obj_t *self) {
234+
return self->used_pins_mask == 0;
235+
}
236+
237+
238+
mp_int_t common_hal_dotclockframebuffer_framebuffer_get_width(dotclockframebuffer_framebuffer_obj_t *self) {
239+
return self->panel_config.timings.h_res;
240+
}
241+
242+
mp_int_t common_hal_dotclockframebuffer_framebuffer_get_height(dotclockframebuffer_framebuffer_obj_t *self) {
243+
return self->panel_config.timings.v_res;
244+
}
245+
246+
mp_int_t common_hal_dotclockframebuffer_framebuffer_get_frequency(dotclockframebuffer_framebuffer_obj_t *self) {
247+
return self->frequency;
248+
}
249+
250+
void common_hal_dotclockframebuffer_framebuffer_refresh(dotclockframebuffer_framebuffer_obj_t *self) {
251+
Cache_WriteBack_Addr((uint32_t)(self->bufinfo.buf), self->bufinfo.len);
252+
}
253+
254+
mp_int_t common_hal_dotclockframebuffer_framebuffer_get_refresh_rate(dotclockframebuffer_framebuffer_obj_t *self) {
255+
return self->refresh_rate;
256+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2023 Jeff Epler for Adafruit Industries
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#pragma once
28+
29+
#include "py/obj.h"
30+
31+
#include "esp_lcd_panel_io.h"
32+
#include "esp_lcd_panel_rgb.h"
33+
#include "esp_lcd_panel_vendor.h"
34+
#include "esp_lcd_panel_ops.h"
35+
#include "esp_lcd_panel_interface.h"
36+
37+
typedef struct dotclockframebuffer_framebuffer_obj {
38+
mp_obj_base_t base;
39+
mp_buffer_info_t bufinfo;
40+
uint32_t frequency, refresh_rate;
41+
uint64_t used_pins_mask;
42+
volatile int32_t frame_count;
43+
esp_lcd_rgb_panel_config_t panel_config;
44+
esp_lcd_panel_handle_t panel_handle;
45+
} dotclockframebuffer_framebuffer_obj_t;

ports/espressif/common-hal/dotclockframebuffer/__init__.c

Whitespace-only changes.

ports/espressif/common-hal/dotclockframebuffer/__init__.h

Whitespace-only changes.

0 commit comments

Comments
 (0)