Skip to content

Commit

Permalink
forking lvgl
Browse files Browse the repository at this point in the history
  • Loading branch information
bwhitman committed May 26, 2024
1 parent 238ab16 commit 0ea5711
Show file tree
Hide file tree
Showing 944 changed files with 661,802 additions and 17 deletions.
4 changes: 0 additions & 4 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,3 @@
[submodule "amy"]
path = amy
url = https://github.com/bwhitman/amy
[submodule "lv_binding_micropython"]
path = lv_binding_micropython
url = https://github.com/bwhitman/lv_binding_micropython

1 change: 0 additions & 1 deletion lv_binding_micropython
Submodule lv_binding_micropython deleted from 53e3ef
21 changes: 21 additions & 0 deletions lv_binding_micropython_tulip/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2019-2021 LVGL

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
509 changes: 509 additions & 0 deletions lv_binding_micropython_tulip/README.md

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions lv_binding_micropython_tulip/bindings.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# This file is to be given as "make USER_C_MODULES=..." when building Micropython port

include(${CMAKE_CURRENT_LIST_DIR}/mkrules.cmake)

# lvgl bindings depend on lvgl itself, pull it in
include(${LVGL_DIR}/CMakeLists.txt)

# lvgl bindings target (the mpy module)
add_library(usermod_lv_bindings INTERFACE)
target_sources(usermod_lv_bindings INTERFACE ${LV_SRC})
target_include_directories(usermod_lv_bindings INTERFACE ${LV_INCLUDE})

target_link_libraries(usermod_lv_bindings INTERFACE lvgl_interface)

# make usermod (target declared by Micropython for all user compiled modules) link to bindings
# this way the bindings (and transitively lvgl_interface) get proper compilation flags
target_link_libraries(usermod INTERFACE usermod_lv_bindings)

# create targets for generated files
all_lv_bindings()
255 changes: 255 additions & 0 deletions lv_binding_micropython_tulip/driver/esp32/espidf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
#include "../include/common.h"
#include "py/obj.h"
#include "py/runtime.h"
#include "py/gc.h"
#include "py/stackctrl.h"
#include "mphalport.h"
#include "espidf.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "soc/cpu.h"


// ESP IDF has some functions that are declared but not implemented.
// To avoid linking errors, provide empty implementation

inline void gpio_pin_wakeup_disable(void){}
inline void gpio_pin_wakeup_enable(uint32_t i, GPIO_INT_TYPE intr_state){}
inline void gpio_intr_ack_high(uint32_t ack_mask){}
inline void gpio_intr_ack(uint32_t ack_mask){}
inline uint32_t gpio_intr_pending_high(void){return 0;}
inline uint32_t gpio_intr_pending(void){return 0;}
inline void gpio_intr_handler_register(gpio_intr_handler_fn_t fn, void *arg){}
inline void gpio_init(void){}

void task_delay_ms(int ms)
{
vTaskDelay(ms / portTICK_RATE_MS);
}

// ISR callbacks handling
// Can't use mp_sched_schedule because lvgl won't yield to give micropython a chance to run
// Must run Micropython in ISR itself.

DEFINE_PTR_OBJ_TYPE(spi_transaction_ptr_type, MP_QSTR_spi_transaction_ptr_t);

typedef struct{
mp_ptr_t spi_transaction_ptr;
mp_obj_t pre_cb;
mp_obj_t post_cb;
} mp_spi_device_callbacks_t;

void *spi_transaction_set_cb(mp_obj_t pre_cb, mp_obj_t post_cb)
{
mp_spi_device_callbacks_t *callbacks = m_new_obj(mp_spi_device_callbacks_t);
callbacks->spi_transaction_ptr.base.type = &spi_transaction_ptr_type;
callbacks->pre_cb = pre_cb != mp_const_none? pre_cb: NULL;
callbacks->post_cb = post_cb != mp_const_none? post_cb: NULL;
return callbacks;
}

STATIC void isr_print_strn(void *env, const char *str, size_t len) {
size_t i;
(void)env;
for (i=0; i<len; i++) ets_write_char_uart(str[i]);
}

static const mp_print_t mp_isr_print = {NULL, isr_print_strn};

// Called in ISR context!
//
static inline void cb_isr(mp_obj_t cb, mp_obj_t arg)
{
if (cb != NULL && cb != mp_const_none) {

volatile uint32_t sp = (uint32_t)get_sp();

// Calling micropython from ISR
// See: https://github.com/micropython/micropython/issues/4895

void *old_state = mp_thread_get_state();

mp_state_thread_t ts; // local thread state for the ISR
mp_thread_set_state(&ts);
mp_stack_set_top((void*)sp); // need to include in root-pointer scan
mp_stack_set_limit(configIDLE_TASK_STACK_SIZE - 1024); // tune based on ISR thread stack size
mp_locals_set(mp_state_ctx.thread.dict_locals); // use main thread's locals
mp_globals_set(mp_state_ctx.thread.dict_globals); // use main thread's globals

mp_sched_lock(); // prevent VM from switching to another MicroPython thread
gc_lock(); // prevent memory allocation

nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
mp_call_function_1(cb, arg);
nlr_pop();
} else {
ets_printf("Uncaught exception in IRQ callback handler!\n");
mp_obj_print_exception(&mp_isr_print, MP_OBJ_FROM_PTR(nlr.ret_val));
}

gc_unlock();
mp_sched_unlock();

mp_thread_set_state(old_state);

mp_hal_wake_main_task_from_isr();
}
}

// Called in ISR context!
void ex_spi_pre_cb_isr(spi_transaction_t *trans)
{
mp_spi_device_callbacks_t *self = (mp_spi_device_callbacks_t*)trans->user;
if (self) {
self->spi_transaction_ptr.ptr = trans;
cb_isr(self->pre_cb, MP_OBJ_FROM_PTR(self));
}
}

// Called in ISR context!
void ex_spi_post_cb_isr(spi_transaction_t *trans)
{
mp_spi_device_callbacks_t *self = (mp_spi_device_callbacks_t*)trans->user;
if (self) {
self->spi_transaction_ptr.ptr = trans;
cb_isr(self->post_cb, MP_OBJ_FROM_PTR(self));
}
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ILI9xxx flush and ISR implementation in C

#include "lvgl/lvgl.h"

DMA_ATTR static uint8_t dma_buf[4] = {0};
DMA_ATTR static spi_transaction_t spi_trans = {0};

static void spi_send_value(const uint8_t *value, uint8_t size, spi_device_handle_t spi)
{
spi_trans.length = size*8;
spi_trans.tx_buffer = value;
spi_trans.user = NULL;
spi_device_polling_transmit(spi, &spi_trans);
}

static inline void ili9xxx_send_cmd(uint8_t cmd, int dc, spi_device_handle_t spi)
{
dma_buf[0] = cmd;
gpio_set_level(dc, 0);
spi_send_value(dma_buf, 1, spi);
}

static inline void ili9xxx_send_data(const uint8_t *value, int dc, spi_device_handle_t spi)
{
gpio_set_level(dc, 1);
spi_send_value(value, 4, spi);
}

static void ili9xxx_send_data_dma(void *disp_drv, void *data, size_t size, int dc, spi_device_handle_t spi)
{
gpio_set_level(dc, 1);
spi_trans.length = size*8;
spi_trans.tx_buffer = data;
spi_trans.user = disp_drv;
spi_device_queue_trans(spi, &spi_trans, -1);
}

void ili9xxx_post_cb_isr(spi_transaction_t *trans)
{
if (trans->user)
lv_display_flush_ready(trans->user);
}


typedef struct {
uint8_t blue;
uint8_t green;
uint8_t red;
} color24_t;

#define DISPLAY_TYPE_ILI9341 1
#define DISPLAY_TYPE_ILI9488 2
#define DISPLAY_TYPE_GC9A01 3
#define DISPLAY_TYPE_ST7789 4
#define DISPLAY_TYPE_ST7735 5

void ili9xxx_flush(void *_disp_drv, const void *_area, void *_color_p)
{
lv_display_t *disp_drv = _disp_drv;
const lv_area_t *area = _area;
lv_color_t *color_p = _color_p;
int start_x = 0;
int start_y = 0;

void *driver_data = lv_display_get_driver_data(disp_drv);

// We use disp_drv->driver_data to pass data from MP to C
// The following lines extract dc and spi

int dc = mp_obj_get_int(mp_obj_dict_get(driver_data, MP_OBJ_NEW_QSTR(MP_QSTR_dc)));
int dt = mp_obj_get_int(mp_obj_dict_get(driver_data, MP_OBJ_NEW_QSTR(MP_QSTR_dt)));
mp_buffer_info_t buffer_info;
mp_get_buffer_raise(mp_obj_dict_get(driver_data, MP_OBJ_NEW_QSTR(MP_QSTR_spi)), &buffer_info, MP_BUFFER_READ);
spi_device_handle_t *spi_ptr = buffer_info.buf;

// Some devices may need a start_x and start_y offset for displays with LCD screens smaller
// than the devices built in frame buffer.

start_x = mp_obj_get_int(mp_obj_dict_get(driver_data, MP_OBJ_NEW_QSTR(MP_QSTR_start_x)));
start_y = mp_obj_get_int(mp_obj_dict_get(driver_data, MP_OBJ_NEW_QSTR(MP_QSTR_start_y)));

int x1 = area->x1 + start_x;
int x2 = area->x2 + start_x;
int y1 = area->y1 + start_y;
int y2 = area->y2 + start_y;

// Column addresses

ili9xxx_send_cmd(0x2A, dc, *spi_ptr);
dma_buf[0] = (x1 >> 8) & 0xFF;
dma_buf[1] = x1 & 0xFF;
dma_buf[2] = (x2 >> 8) & 0xFF;
dma_buf[3] = x2 & 0xFF;

ili9xxx_send_data(dma_buf, dc, *spi_ptr);

// Page addresses

ili9xxx_send_cmd(0x2B, dc, *spi_ptr);
dma_buf[0] = (y1 >> 8) & 0xFF;
dma_buf[1] = y1 & 0xFF;
dma_buf[2] = (y2 >> 8) & 0xFF;
dma_buf[3] = y2 & 0xFF;

ili9xxx_send_data(dma_buf, dc, *spi_ptr);

// Memory write by DMA, disp_flush_ready when finished

ili9xxx_send_cmd(0x2C, dc, *spi_ptr);

size_t size = (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1);
uint8_t color_size = 2;

bool swap_rgb565_bytes = mp_obj_get_int(mp_obj_dict_get(driver_data, MP_OBJ_NEW_QSTR(MP_QSTR_swap_rgb565_bytes)));
if ( swap_rgb565_bytes == true ) {
lv_draw_sw_rgb565_swap(color_p, size);
}

if ( dt == DISPLAY_TYPE_ILI9488 ) {
color_size = 3;
/*Convert ARGB to RGB is required (cut off A-byte)*/
size_t i;
lv_color32_t* tmp32 = (lv_color32_t*) color_p;
color24_t* tmp24 = (color24_t*) color_p;

for(i=0; i < size; i++) {
tmp24[i].red = tmp32[i].red;
tmp24[i].green = tmp32[i].green;
tmp24[i].blue = tmp32[i].blue;
}
}

ili9xxx_send_data_dma(disp_drv, color_p, size * color_size, dc, *spi_ptr);
}
Loading

0 comments on commit 0ea5711

Please sign in to comment.