├── .github ├── auto-comment.yml └── workflows │ └── unix_port.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── bindings.cmake ├── driver ├── esp32 │ ├── espidf.c │ ├── espidf.h │ ├── ili9341.py │ ├── ili9XXX.py │ ├── lv_spi.py │ ├── modrtch.c │ ├── modxpt2046.c │ ├── sh2lib.c │ ├── sh2lib.h │ └── xpt2046.py ├── generic │ ├── axp192.py │ ├── ft6x36.py │ ├── ili9xxx-test.py │ ├── ili9xxx.py │ ├── st77xx-test.py │ ├── st77xx.py │ ├── xpt2046-test.py │ └── xpt2046.py ├── include │ └── common.h ├── js │ └── lv_timer.py ├── linux │ ├── evdev.py │ └── lv_timer.py ├── rp2 │ ├── rp2_dma-test.py │ └── rp2_dma.py ├── stm32 │ ├── STM32F7DISC │ │ ├── ft5336.c │ │ ├── ft5336.h │ │ ├── modrk043fn48h.c │ │ ├── rk043fn48h.h │ │ ├── stm32746g_discovery.h │ │ ├── stm32746g_discovery_ts.c │ │ ├── stm32746g_discovery_ts.h │ │ └── ts.h │ └── string1.c └── zephyr │ ├── README.md │ ├── lvgl.c │ ├── lvgl_driver.h │ └── modlvzephyr.c ├── examples ├── Dynamic_loading_font_example.py ├── advanced_demo.py ├── blue_flower_32.bin ├── custom_widget_example.py ├── example1.py ├── example3.py ├── fb_test.py ├── font │ ├── font-PHT-cn-20.bin │ ├── font-PHT-en-20.bin │ └── font-PHT-jp-20.bin ├── generic-st77xx-with-xpt2046.py ├── madctl │ ├── README.md │ ├── images │ │ ├── madctl_0.png │ │ ├── madctl_v.png │ │ ├── madctl_vx.png │ │ ├── madctl_vxy.png │ │ ├── madctl_vy.png │ │ ├── madctl_x.png │ │ ├── madctl_xy.png │ │ └── madctl_y.png │ └── madctl_helper.py ├── png_decoder_test.png ├── png_example.py └── uasyncio_example1.py ├── gen ├── .gitignore ├── gen_mpy.py ├── lv_mpy_example.c └── lv_mpy_example.json ├── include ├── lv_mp_mem_custom_include.h └── lv_mp_root_pointers.h ├── lib ├── display_driver.py ├── display_driver_utils.py ├── fs_driver.py ├── lv_colors.py ├── lv_utils.py ├── tpcal.py └── utils.py ├── lv_conf.h ├── mkrules.cmake └── tests ├── run.sh └── run_test.py /.github/auto-comment.yml: -------------------------------------------------------------------------------- 1 | # Comment to a new issue. 2 | pullRequestOpened: | 3 | Thank you for raising your pull request. 4 | 5 | To ensure that all licensing criteria is met all repositories of the LVGL project apply a process called DCO (Developer's Certificate of Origin). 6 | 7 | The text of DCO can be read here: https://developercertificate.org/ 8 | For a more detailed description see the [Documentation](https://docs.lvgl.io/latest/en/html/contributing/index.html#developer-certification-of-origin-dco) site. 9 | 10 | By contributing to any repositories of the LVGL project you state that your contribution corresponds with the DCO. 11 | 12 | No further action is required if your contribution fulfills the DCO. If you are not sure about it feel free to ask us in a comment. 13 | -------------------------------------------------------------------------------- /.github/workflows/unix_port.yml: -------------------------------------------------------------------------------- 1 | name: Build lv_micropython unix port 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: ubuntu-20.04 11 | 12 | steps: 13 | - name: Install Dependencies 14 | run: | 15 | sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse" 16 | sudo apt-get update -y -qq 17 | sudo apt-get install libsdl2-dev parallel libfreetype-dev librlottie-dev libavformat-dev libavcodec-dev libswscale-dev libavutil-dev 18 | - name: Clone lv_micropython 19 | run: | 20 | git clone https://github.com/lvgl/lv_micropython.git . 21 | git checkout master 22 | - name: Initialize lv_bindings submodule 23 | run: git submodule update --init --recursive lib/lv_bindings 24 | - name: Update Unix port submodules 25 | run: make -C ports/unix DEBUG=1 submodules 26 | - name: Checkout lv_bindings 27 | working-directory: ./lib/lv_bindings 28 | run: | 29 | git fetch --force ${{ github.event.repository.html_url }} "+refs/heads/*:refs/remotes/origin/*" 30 | git fetch --force ${{ github.event.repository.html_url }} "+refs/pull/*:refs/remotes/origin/pr/*" 31 | git checkout ${{ github.sha }} || git checkout ${{ github.event.pull_request.head.sha }} 32 | git submodule update --init --recursive 33 | - name: Build mpy-cross 34 | run: make -j $(nproc) -C mpy-cross 35 | - name: Build the unix port 36 | run: make -j $(nproc) -C ports/unix DEBUG=1 37 | - name: Run tests 38 | run: | 39 | export XDG_RUNTIME_DIR=/tmp 40 | lib/lv_bindings/tests/run.sh 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lextab.py 2 | yacctab.py 3 | *.swp 4 | *.swo 5 | *.swn 6 | *.bak 7 | .history 8 | .vscode 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lvgl"] 2 | path = lvgl 3 | url = https://github.com/lvgl/lvgl.git 4 | [submodule "micropython/pycparser"] 5 | path = pycparser 6 | url = https://github.com/eliben/pycparser.git 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2021 LVGL 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /bindings.cmake: -------------------------------------------------------------------------------- 1 | # This file is to be given as "make USER_C_MODULES=..." when building Micropython port 2 | 3 | include(${CMAKE_CURRENT_LIST_DIR}/mkrules.cmake) 4 | 5 | # lvgl bindings depend on lvgl itself, pull it in 6 | include(${LVGL_DIR}/CMakeLists.txt) 7 | 8 | # lvgl bindings target (the mpy module) 9 | add_library(usermod_lv_bindings INTERFACE) 10 | target_sources(usermod_lv_bindings INTERFACE ${LV_SRC}) 11 | target_include_directories(usermod_lv_bindings INTERFACE ${LV_INCLUDE}) 12 | 13 | target_link_libraries(usermod_lv_bindings INTERFACE lvgl_interface) 14 | 15 | # make usermod (target declared by Micropython for all user compiled modules) link to bindings 16 | # this way the bindings (and transitively lvgl_interface) get proper compilation flags 17 | target_link_libraries(usermod INTERFACE usermod_lv_bindings) 18 | 19 | # create targets for generated files 20 | all_lv_bindings() 21 | -------------------------------------------------------------------------------- /driver/esp32/espidf.c: -------------------------------------------------------------------------------- 1 | #include "../include/common.h" 2 | #include "py/obj.h" 3 | #include "py/runtime.h" 4 | #include "py/gc.h" 5 | #include "py/stackctrl.h" 6 | #include "mphalport.h" 7 | #include "espidf.h" 8 | #include "freertos/FreeRTOS.h" 9 | #include "freertos/task.h" 10 | #include "esp_system.h" 11 | #include "soc/cpu.h" 12 | 13 | 14 | // ESP IDF has some functions that are declared but not implemented. 15 | // To avoid linking errors, provide empty implementation 16 | 17 | inline void gpio_pin_wakeup_disable(void){} 18 | inline void gpio_pin_wakeup_enable(uint32_t i, GPIO_INT_TYPE intr_state){} 19 | inline void gpio_intr_ack_high(uint32_t ack_mask){} 20 | inline void gpio_intr_ack(uint32_t ack_mask){} 21 | inline uint32_t gpio_intr_pending_high(void){return 0;} 22 | inline uint32_t gpio_intr_pending(void){return 0;} 23 | inline void gpio_intr_handler_register(gpio_intr_handler_fn_t fn, void *arg){} 24 | inline void gpio_init(void){} 25 | 26 | void task_delay_ms(int ms) 27 | { 28 | vTaskDelay(ms / portTICK_RATE_MS); 29 | } 30 | 31 | // ISR callbacks handling 32 | // Can't use mp_sched_schedule because lvgl won't yield to give micropython a chance to run 33 | // Must run Micropython in ISR itself. 34 | 35 | DEFINE_PTR_OBJ_TYPE(spi_transaction_ptr_type, MP_QSTR_spi_transaction_ptr_t); 36 | 37 | typedef struct{ 38 | mp_ptr_t spi_transaction_ptr; 39 | mp_obj_t pre_cb; 40 | mp_obj_t post_cb; 41 | } mp_spi_device_callbacks_t; 42 | 43 | void *spi_transaction_set_cb(mp_obj_t pre_cb, mp_obj_t post_cb) 44 | { 45 | mp_spi_device_callbacks_t *callbacks = m_new_obj(mp_spi_device_callbacks_t); 46 | callbacks->spi_transaction_ptr.base.type = &spi_transaction_ptr_type; 47 | callbacks->pre_cb = pre_cb != mp_const_none? pre_cb: NULL; 48 | callbacks->post_cb = post_cb != mp_const_none? post_cb: NULL; 49 | return callbacks; 50 | } 51 | 52 | STATIC void isr_print_strn(void *env, const char *str, size_t len) { 53 | size_t i; 54 | (void)env; 55 | for (i=0; iuser; 105 | if (self) { 106 | self->spi_transaction_ptr.ptr = trans; 107 | cb_isr(self->pre_cb, MP_OBJ_FROM_PTR(self)); 108 | } 109 | } 110 | 111 | // Called in ISR context! 112 | void ex_spi_post_cb_isr(spi_transaction_t *trans) 113 | { 114 | mp_spi_device_callbacks_t *self = (mp_spi_device_callbacks_t*)trans->user; 115 | if (self) { 116 | self->spi_transaction_ptr.ptr = trans; 117 | cb_isr(self->post_cb, MP_OBJ_FROM_PTR(self)); 118 | } 119 | } 120 | 121 | ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 122 | // ILI9xxx flush and ISR implementation in C 123 | 124 | #include "lvgl/lvgl.h" 125 | 126 | DMA_ATTR static uint8_t dma_buf[4] = {0}; 127 | DMA_ATTR static spi_transaction_t spi_trans = {0}; 128 | 129 | static void spi_send_value(const uint8_t *value, uint8_t size, spi_device_handle_t spi) 130 | { 131 | spi_trans.length = size*8; 132 | spi_trans.tx_buffer = value; 133 | spi_trans.user = NULL; 134 | spi_device_polling_transmit(spi, &spi_trans); 135 | } 136 | 137 | static inline void ili9xxx_send_cmd(uint8_t cmd, int dc, spi_device_handle_t spi) 138 | { 139 | dma_buf[0] = cmd; 140 | gpio_set_level(dc, 0); 141 | spi_send_value(dma_buf, 1, spi); 142 | } 143 | 144 | static inline void ili9xxx_send_data(const uint8_t *value, int dc, spi_device_handle_t spi) 145 | { 146 | gpio_set_level(dc, 1); 147 | spi_send_value(value, 4, spi); 148 | } 149 | 150 | static void ili9xxx_send_data_dma(void *disp_drv, void *data, size_t size, int dc, spi_device_handle_t spi) 151 | { 152 | gpio_set_level(dc, 1); 153 | spi_trans.length = size*8; 154 | spi_trans.tx_buffer = data; 155 | spi_trans.user = disp_drv; 156 | spi_device_queue_trans(spi, &spi_trans, -1); 157 | } 158 | 159 | void ili9xxx_post_cb_isr(spi_transaction_t *trans) 160 | { 161 | if (trans->user) 162 | lv_disp_flush_ready(trans->user); 163 | } 164 | 165 | 166 | typedef struct { 167 | uint8_t blue; 168 | uint8_t green; 169 | uint8_t red; 170 | } color24_t; 171 | 172 | #define DISPLAY_TYPE_ILI9341 1 173 | #define DISPLAY_TYPE_ILI9488 2 174 | #define DISPLAY_TYPE_GC9A01 3 175 | #define DISPLAY_TYPE_ST7789 4 176 | #define DISPLAY_TYPE_ST7735 5 177 | 178 | void ili9xxx_flush(void *_disp_drv, const void *_area, void *_color_p) 179 | { 180 | lv_disp_t *disp_drv = _disp_drv; 181 | const lv_area_t *area = _area; 182 | lv_color_t *color_p = _color_p; 183 | int start_x = 0; 184 | int start_y = 0; 185 | 186 | void *driver_data = lv_disp_get_driver_data(disp_drv); 187 | 188 | // We use disp_drv->driver_data to pass data from MP to C 189 | // The following lines extract dc and spi 190 | 191 | int dc = mp_obj_get_int(mp_obj_dict_get(driver_data, MP_OBJ_NEW_QSTR(MP_QSTR_dc))); 192 | int dt = mp_obj_get_int(mp_obj_dict_get(driver_data, MP_OBJ_NEW_QSTR(MP_QSTR_dt))); 193 | mp_buffer_info_t buffer_info; 194 | mp_get_buffer_raise(mp_obj_dict_get(driver_data, MP_OBJ_NEW_QSTR(MP_QSTR_spi)), &buffer_info, MP_BUFFER_READ); 195 | spi_device_handle_t *spi_ptr = buffer_info.buf; 196 | 197 | // Some devices may need a start_x and start_y offset for displays with LCD screens smaller 198 | // than the devices built in frame buffer. 199 | 200 | start_x = mp_obj_get_int(mp_obj_dict_get(driver_data, MP_OBJ_NEW_QSTR(MP_QSTR_start_x))); 201 | start_y = mp_obj_get_int(mp_obj_dict_get(driver_data, MP_OBJ_NEW_QSTR(MP_QSTR_start_y))); 202 | 203 | int x1 = area->x1 + start_x; 204 | int x2 = area->x2 + start_x; 205 | int y1 = area->y1 + start_y; 206 | int y2 = area->y2 + start_y; 207 | 208 | // Column addresses 209 | 210 | ili9xxx_send_cmd(0x2A, dc, *spi_ptr); 211 | dma_buf[0] = (x1 >> 8) & 0xFF; 212 | dma_buf[1] = x1 & 0xFF; 213 | dma_buf[2] = (x2 >> 8) & 0xFF; 214 | dma_buf[3] = x2 & 0xFF; 215 | 216 | ili9xxx_send_data(dma_buf, dc, *spi_ptr); 217 | 218 | // Page addresses 219 | 220 | ili9xxx_send_cmd(0x2B, dc, *spi_ptr); 221 | dma_buf[0] = (y1 >> 8) & 0xFF; 222 | dma_buf[1] = y1 & 0xFF; 223 | dma_buf[2] = (y2 >> 8) & 0xFF; 224 | dma_buf[3] = y2 & 0xFF; 225 | 226 | ili9xxx_send_data(dma_buf, dc, *spi_ptr); 227 | 228 | // Memory write by DMA, disp_flush_ready when finished 229 | 230 | ili9xxx_send_cmd(0x2C, dc, *spi_ptr); 231 | 232 | size_t size = (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1); 233 | uint8_t color_size = 2; 234 | 235 | if ( dt == DISPLAY_TYPE_ILI9488 ) { 236 | color_size = 3; 237 | /*Convert ARGB to RGB is required (cut off A-byte)*/ 238 | size_t i; 239 | lv_color32_t* tmp32 = (lv_color32_t*) color_p; 240 | color24_t* tmp24 = (color24_t*) color_p; 241 | 242 | for(i=0; i < size; i++) { 243 | tmp24[i].red = tmp32[i].red; 244 | tmp24[i].green = tmp32[i].green; 245 | tmp24[i].blue = tmp32[i].blue; 246 | } 247 | } 248 | 249 | ili9xxx_send_data_dma(disp_drv, color_p, size * color_size, dc, *spi_ptr); 250 | } 251 | -------------------------------------------------------------------------------- /driver/esp32/espidf.h: -------------------------------------------------------------------------------- 1 | /** 2 | * This file defines the Micorpython API to ESP-IDF 3 | * It is used as input to gen_mpy.py to create a micropython module 4 | **/ 5 | #if __has_include("esp_idf_version.h") 6 | # include "esp_idf_version.h" 7 | #endif 8 | 9 | // Disable some macros and includes that make pycparser choke 10 | 11 | #ifdef PYCPARSER 12 | #define __attribute__(x) 13 | #define _Static_assert(x,y) 14 | #define __extension__ 15 | #define _SOC_IO_MUX_REG_H_ 16 | #define _SYS_REENT_H_ 17 | #define PORTMACRO_H 18 | #define PORTABLE_H 19 | #define INC_FREERTOS_H 20 | #define QUEUE_H 21 | #define SEMAPHORE_H 22 | #define XTENSA_HAL_H 23 | #define _SOC_I2S_STRUCT_H_ 24 | #define XTRUNTIME_H 25 | #define _SOC_SPI_STRUCT_H_ 26 | #define _SOC_RTC_CNTL_STRUCT_H_ 27 | #define __XTENSA_API_H__ 28 | #define _SOC_GPIO_STRUCT_H_ 29 | #define _SOC_RTC_IO_STRUCT_H_ 30 | #define _SOC_PCNT_STRUCT_H_ 31 | #define _SYS_FCNTL_H_ 32 | #define __SYS_ARCH_H__ 33 | #define LIST_H 34 | #define INC_TASK_H 35 | #define LWIP_HDR_NETIF_H 36 | #define ESP_EVENT_H_ 37 | #define __SNTP_H__ 38 | #define XTENSA_CONFIG_CORE_H 39 | #define _SOC_SPI_MEM_STRUCT_H_ 40 | 41 | typedef int BaseType_t; 42 | typedef unsigned int UBaseType_t; 43 | typedef void* system_event_t; 44 | typedef void *intr_handle_t; 45 | 46 | // Exclude SOC just because it contains large structs that don't interest the user 47 | #define _SOC_SPI_PERIPH_H_ 48 | typedef void *spi_dev_t; 49 | 50 | // TODO: Check why lldesc_t causes infinite recursion on gen_mpy.py 51 | #define _ROM_LLDESC_H_ 52 | typedef void *lldesc_t; 53 | 54 | // FreeRTOS definitions we want available on Micropython 55 | #include 56 | typedef uint32_t TickType_t; 57 | typedef void * TaskHandle_t; 58 | static inline uint32_t xPortGetCoreID(); 59 | UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask ); 60 | 61 | // Micropython specific types 62 | typedef void *mp_obj_t; 63 | 64 | static inline void SPH0645_WORKAROUND(int i2s_num); 65 | static inline void get_ccount(int *ccount); 66 | 67 | // Memory management helper functions 68 | void * memcpy ( void * destination, const void * source, size_t num ); 69 | void * memset ( void * ptr, int value, size_t num ); 70 | 71 | 72 | #else // PYCPARSER 73 | 74 | 75 | ///////////////////////////////////////////////////////////////////////////////////////////// 76 | // A workaround for SPH0645 I2S, see: 77 | // - https://hackaday.io/project/162059-street-sense/log/160705-new-i2s-microphone/discussion-124677 78 | // - https://www.esp32.com/viewtopic.php?t=4997#p45366 79 | // Since reg access is based on macros, this cannot currently be directly implemented in Micropython 80 | 81 | #include "soc/i2s_reg.h" // for SPH0645_WORKAROUND 82 | 83 | static inline void SPH0645_WORKAROUND(int i2s_num) 84 | { 85 | REG_SET_BIT( I2S_TIMING_REG(i2s_num), BIT(9)); 86 | REG_SET_BIT( I2S_CONF_REG(i2s_num), I2S_RX_MSB_SHIFT); 87 | } 88 | 89 | ///////////////////////////////////////////////////////////////////////////////////////////// 90 | // Helper function to measure CPU cycles 91 | // 92 | static inline void get_ccount(int *ccount) 93 | { 94 | asm volatile("rsr.ccount %0" : "=a"(*ccount)); 95 | } 96 | 97 | 98 | #endif //PYCPARSER 99 | 100 | // The following includes are the source of the esp-idf micropython module. 101 | // All included files are API we want to include in the module 102 | 103 | #if defined(ESP_IDF_VERSION_MAJOR) && ESP_IDF_VERSION_MAJOR >= 4 104 | # if CONFIG_IDF_TARGET_ESP32 105 | # include "esp32/clk.h" 106 | # elif CONFIG_IDF_TARGET_ESP32S2 107 | # include "esp32s2/clk.h" 108 | # elif CONFIG_IDF_TARGET_ESP32S3 109 | # include "esp32s3/clk.h" 110 | # elif CONFIG_IDF_TARGET_ESP32C3 111 | # include "esp32c3/clk.h" 112 | # elif CONFIG_IDF_TARGET_ESP32H2 113 | # include "esp32h2/clk.h" 114 | # else // CONFIG_IDF_TARGET_* not defined 115 | # include "esp32/clk.h" 116 | # endif 117 | #else 118 | # include "esp_clk.h" 119 | #endif 120 | 121 | #include "driver/gpio.h" 122 | #include "driver/spi_master.h" 123 | #include "esp_heap_caps.h" 124 | #include "esp_log.h" 125 | #include "driver/adc.h" 126 | #include "driver/i2s.h" 127 | #include "driver/pcnt.h" 128 | #include "mdns.h" 129 | #include "esp_http_client.h" 130 | #include "sh2lib.h" 131 | 132 | ///////////////////////////////////////////////////////////////////////////////////////////// 133 | // Helper function to register HTTP event handler 134 | // Needed to fulfill gen_mpy.py callback conventions 135 | // 136 | static inline void esp_http_client_register_event_handler(esp_http_client_config_t *config, http_event_handle_cb http_event_handler, void *user_data) 137 | { 138 | config->event_handler = http_event_handler; 139 | config->user_data = user_data; 140 | } 141 | 142 | // We don't want the whole FreeRTOS, only selected functions 143 | 144 | void task_delay_ms(int ms); 145 | 146 | // The binding only publishes structs that are used in some function. We need spi_transaction_ext_t 147 | // TODO: Find some way to mark structs for binding export instead of new function. 148 | static inline void set_spi_transaction_ext( 149 | spi_transaction_ext_t *ext_trans, 150 | spi_transaction_t *trans, 151 | uint8_t command_bits, 152 | uint8_t address_bits){ 153 | ext_trans->base = *trans; 154 | ext_trans->command_bits = command_bits; 155 | ext_trans->address_bits = address_bits; 156 | } 157 | 158 | // Wrapper for safe ISR callbacks from micropython 159 | // Need to call both spi_transaction_set_cb and set spi_pre/post_cb_isr! 160 | 161 | // Use this to set pre/post callbacks for spi transaction. 162 | // pre_cb/post_cb should be either a callable object or "None". 163 | // Result should be set to spi_transaction_t user field. 164 | // Allocates RAM. 165 | 166 | void *spi_transaction_set_cb(mp_obj_t pre_cb, mp_obj_t post_cb); 167 | 168 | // These functions can be set into pre_cb/post_cb of spi_device_interface_config_t 169 | 170 | void ex_spi_pre_cb_isr(spi_transaction_t *trans); 171 | void ex_spi_post_cb_isr(spi_transaction_t *trans); 172 | 173 | // Useful constants 174 | 175 | #define EXPORT_CONST_INT(int_value) enum {ENUM_##int_value = int_value} 176 | 177 | #if defined(ESP_IDF_VERSION_MAJOR) && ESP_IDF_VERSION_MAJOR >= 4 178 | // SPI HOST enum was changed to macros on v4 179 | enum { 180 | ENUM_SPI_HOST = SPI_HOST, 181 | ENUM_HSPI_HOST = HSPI_HOST, 182 | ENUM_VSPI_HOST = VSPI_HOST, 183 | }; 184 | #endif 185 | 186 | enum { 187 | ENUM_portMAX_DELAY = portMAX_DELAY 188 | }; 189 | 190 | enum { 191 | ENUM_I2S_PIN_NO_CHANGE = I2S_PIN_NO_CHANGE 192 | }; 193 | 194 | enum { 195 | ENUM_SPI_DEVICE_TXBIT_LSBFIRST = SPI_DEVICE_TXBIT_LSBFIRST, 196 | ENUM_SPI_DEVICE_RXBIT_LSBFIRST = SPI_DEVICE_RXBIT_LSBFIRST, 197 | ENUM_SPI_DEVICE_BIT_LSBFIRST = SPI_DEVICE_BIT_LSBFIRST, 198 | ENUM_SPI_DEVICE_3WIRE = SPI_DEVICE_3WIRE, 199 | ENUM_SPI_DEVICE_POSITIVE_CS = SPI_DEVICE_POSITIVE_CS, 200 | ENUM_SPI_DEVICE_HALFDUPLEX = SPI_DEVICE_HALFDUPLEX, 201 | ENUM_SPI_DEVICE_NO_DUMMY = SPI_DEVICE_NO_DUMMY, 202 | ENUM_SPI_DEVICE_CLK_AS_CS = SPI_DEVICE_CLK_AS_CS, 203 | }; 204 | 205 | enum { 206 | ENUM_SPI_TRANS_MODE_DIO = SPI_TRANS_MODE_DIO, 207 | ENUM_SPI_TRANS_MODE_QIO = SPI_TRANS_MODE_QIO, 208 | ENUM_SPI_TRANS_MODE_DIOQIO_ADDR = SPI_TRANS_MODE_DIOQIO_ADDR, 209 | ENUM_SPI_TRANS_USE_RXDATA = SPI_TRANS_USE_RXDATA, 210 | ENUM_SPI_TRANS_USE_TXDATA = SPI_TRANS_USE_TXDATA, 211 | ENUM_SPI_TRANS_VARIABLE_CMD = SPI_TRANS_VARIABLE_CMD, 212 | ENUM_SPI_TRANS_VARIABLE_ADDR = SPI_TRANS_VARIABLE_ADDR, 213 | }; 214 | 215 | enum { 216 | ENUM_MALLOC_CAP_EXEC = MALLOC_CAP_EXEC, 217 | ENUM_MALLOC_CAP_32BIT = MALLOC_CAP_32BIT, 218 | ENUM_MALLOC_CAP_8BIT = MALLOC_CAP_8BIT, 219 | ENUM_MALLOC_CAP_DMA = MALLOC_CAP_DMA, 220 | ENUM_MALLOC_CAP_SPIRAM = MALLOC_CAP_SPIRAM, 221 | ENUM_MALLOC_CAP_INTERNAL = MALLOC_CAP_INTERNAL, 222 | ENUM_MALLOC_CAP_DEFAULT = MALLOC_CAP_DEFAULT, 223 | // Missing on espidf v4.02: 224 | // ENUM_MALLOC_CAP_IRAM_8BIT = MALLOC_CAP_IRAM_8BIT, 225 | ENUM_MALLOC_CAP_INVALID = MALLOC_CAP_INVALID, 226 | }; 227 | 228 | enum { 229 | ENUM_ESP_TASK_PRIO_MAX = ESP_TASK_PRIO_MAX, 230 | ENUM_ESP_TASK_PRIO_MIN = ESP_TASK_PRIO_MIN, 231 | }; 232 | 233 | ///////////////////////////////////////////////////////////////////////////////////////////// 234 | // ili9xxx flush and ISR in C 235 | // 236 | // disp_drv->user_data should be a dict that contains dc and spi, setup by micropython. 237 | // like this: "self.disp_drv.user_data = {'dc': self.dc, 'spi': self.spi, 'dt': display_type}" 238 | 239 | 240 | void ili9xxx_post_cb_isr(spi_transaction_t *trans); 241 | 242 | void ili9xxx_flush(void *disp_drv, const void *area, void *color_p); 243 | 244 | 245 | -------------------------------------------------------------------------------- /driver/esp32/ili9341.py: -------------------------------------------------------------------------------- 1 | ############################################################################## 2 | # 3 | # Wrapper function for backward compatibility with new ILI9XXX library. 4 | # 5 | ############################################################################## 6 | 7 | print(""" 8 | *************************************** 9 | * This library is obsoled now! 10 | * Please, use ili9XXX library instead: 11 | * 12 | * from ili9XXX import ili9341 13 | * 14 | *************************************** 15 | """) 16 | 17 | from ili9XXX import ili9341 18 | 19 | -------------------------------------------------------------------------------- /driver/esp32/lv_spi.py: -------------------------------------------------------------------------------- 1 | """ 2 | MicroPython espidf SPI driver 3 | Used, for example, to share spi bus between SPI screen and sdcard.py driver 4 | 5 | Provides simple init, deinit, write, read, readinto and write_readinto methods 6 | 7 | Example usage with sdcard.py driver on ESP32 : 8 | 9 | import machine, os, lv_spi, sdcard 10 | spi = lv_spi.SPI(mosi=23, miso=38, clk=18) 11 | sd = sdcard.SDCard(spi, machine.Pin(4)) 12 | os.mount(sd, "/sd") 13 | print(os.listdir('/sd')) 14 | 15 | Example usage with ili93XXX.py + sdcard.py drivers (SPI bus sharing) : 16 | 17 | import machine, os, lv_spi, sdcard 18 | from ili9XXX import ili9341 19 | lcd = ili9341(mosi=23, miso=38, clk=18, dc=15, cs=5, invert=True, rot=0x10, width=320, height=240, 20 | half_duplex=False) # half_duplex do not work with SDCard 21 | sd = sdcard.SDCard(lv_spi.SPI(), machine.Pin(4)) 22 | os.mount(sd, "/sd") 23 | print(os.listdir('/sd')) 24 | 25 | """ 26 | 27 | import espidf as esp 28 | 29 | class SPI(object): 30 | 31 | SPI_HOST = esp.SPI_HOST 32 | HSPI_HOST = esp.HSPI_HOST 33 | VSPI_HOST = esp.VSPI_HOST 34 | 35 | def __init__(self, spihost=HSPI_HOST, baudrate=200000, miso=-1, mosi=-1, clk=-1, cs=-1, 36 | phase=0, polarity=0, half_duplex=False, max_transfer_sz=4092): 37 | 38 | self.spihost = spihost 39 | self.baudrate = baudrate 40 | self.miso = miso 41 | self.mosi = mosi 42 | self.clk = clk 43 | self.cs = cs 44 | self.phase = phase 45 | self.polarity = polarity 46 | self.half_duplex = half_duplex 47 | self.max_transfer_sz = max_transfer_sz 48 | 49 | self.spi = None 50 | 51 | buscfg = esp.spi_bus_config_t( 52 | { 53 | "miso_io_num": self.miso, 54 | "mosi_io_num": self.mosi, 55 | "sclk_io_num": self.clk, 56 | "quadwp_io_num": -1, 57 | "quadhd_io_num": -1, 58 | "max_transfer_sz": max_transfer_sz, 59 | } 60 | ) 61 | 62 | # Initialize the SPI bus, if needed. 63 | if buscfg.miso_io_num >= 0 and buscfg.mosi_io_num >= 0 and buscfg.sclk_io_num >= 0: 64 | 65 | esp.gpio_pad_select_gpio(self.miso) 66 | esp.gpio_pad_select_gpio(self.mosi) 67 | esp.gpio_pad_select_gpio(self.clk) 68 | 69 | esp.gpio_set_direction(self.miso, esp.GPIO_MODE.INPUT) 70 | esp.gpio_set_pull_mode(self.miso, esp.GPIO.PULLUP_ONLY) 71 | esp.gpio_set_direction(self.mosi, esp.GPIO_MODE.OUTPUT) 72 | esp.gpio_set_direction(self.clk, esp.GPIO_MODE.OUTPUT) 73 | 74 | ret = esp.spi_bus_initialize(self.spihost, buscfg, 1) 75 | if ret != 0: 76 | raise RuntimeError("Failed initializing SPI bus") 77 | 78 | 79 | def init(self, baudrate=None, phase=None, polarity=None, cs=-1): 80 | 81 | if baudrate: 82 | self.baudrate = baudrate 83 | 84 | if phase: 85 | self.phase = phase 86 | 87 | if polarity: 88 | self.polarity = polarity 89 | 90 | if cs > -1: 91 | self.cs=cs 92 | 93 | if self.spi: 94 | # Remove device (to change device configuration) 95 | esp.spi_bus_remove_device(self.spi) 96 | self.spi = None 97 | 98 | 99 | devcfg_flags = esp.SPI_DEVICE.NO_DUMMY 100 | if self.half_duplex : devcfg_flags |= esp.SPI_DEVICE.HALFDUPLEX 101 | 102 | devcfgSD = esp.spi_device_interface_config_t({ 103 | "clock_speed_hz": self.baudrate, 104 | "mode": self.phase | self.polarity << 1, 105 | "spics_io_num": self.cs, 106 | "queue_size": 2, 107 | "flags": devcfg_flags, 108 | "duty_cycle_pos": 128, 109 | }) 110 | 111 | if cs > -1 : 112 | esp.gpio_pad_select_gpio(self.cs) 113 | 114 | # Attach device to the SPI bus 115 | ptr_to_spi = esp.C_Pointer() 116 | ret = esp.spi_bus_add_device(self.spihost, devcfgSD, ptr_to_spi) 117 | if ret != 0: 118 | raise RuntimeError("Failed adding SPI device") 119 | 120 | self.spi = ptr_to_spi.ptr_val 121 | self.trans_result_ptr = esp.C_Pointer() 122 | 123 | 124 | def write(self, data): 125 | if not isinstance(data,bytearray) : data = bytearray(data) 126 | trans = esp.spi_transaction_t({ 127 | "length": len(data) * 8, 128 | "tx_buffer": data, 129 | "rx_buffer": None, 130 | "user": None, 131 | }) 132 | esp.spi_device_queue_trans(self.spi, trans, -1) 133 | esp.spi_device_get_trans_result(self.spi, self.trans_result_ptr , -1) 134 | 135 | 136 | def read(self, length:int, write=None): 137 | if write: write = bytearray([write]) 138 | buf=bytearray(length) 139 | trans = esp.spi_transaction_t({ 140 | "length": length * 8, 141 | "tx_buffer": write, 142 | "rx_buffer": buf, 143 | "user": None, 144 | }) 145 | esp.spi_device_queue_trans(self.spi, trans, -1) 146 | esp.spi_device_get_trans_result(self.spi, self.trans_result_ptr , -1) 147 | return bytes(buf) 148 | 149 | 150 | def readinto(self, buf:bytearray, write=None): 151 | if write: write = bytearray([write]) 152 | trans = esp.spi_transaction_t({ 153 | "length": len(buf) * 8, 154 | "tx_buffer": write, 155 | "rx_buffer": buf, 156 | "user": None, 157 | }) 158 | esp.spi_device_queue_trans(self.spi, trans, -1) 159 | esp.spi_device_get_trans_result(self.spi, self.trans_result_ptr , -1) 160 | 161 | 162 | def write_readinto(self, data, buf:bytearray): 163 | if not isinstance(data,bytearray) : data = bytearray(data) 164 | trans = esp.spi_transaction_t({ 165 | "length": len(data) * 8, 166 | "tx_buffer": data, 167 | "rx_buffer": buf, 168 | "user": None, 169 | }) 170 | esp.spi_device_queue_trans(self.spi, trans, -1) 171 | esp.spi_device_get_trans_result(self.spi, self.trans_result_ptr , -1) 172 | 173 | 174 | def deinit(self): 175 | 176 | if self.spi: 177 | # Remove device 178 | esp.spi_bus_remove_device(self.spi) 179 | self.spi = None 180 | 181 | if self.miso >= 0 and self.mosi >= 0 and self.clk >= 0: 182 | # Free SPI bus 183 | esp.spi_bus_free(self.spihost) 184 | self.spihost = None 185 | 186 | -------------------------------------------------------------------------------- /driver/esp32/modxpt2046.c: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // Includes 3 | ////////////////////////////////////////////////////////////////////////////// 4 | 5 | #include "../include/common.h" 6 | #include "lvgl/src/lv_hal/lv_hal_indev.h" 7 | #include "lvgl/src/lv_core/lv_disp.h" 8 | #include "py/obj.h" 9 | #include "py/runtime.h" 10 | #include "esp_system.h" 11 | #include "driver/gpio.h" 12 | #include "driver/spi_master.h" 13 | 14 | ////////////////////////////////////////////////////////////////////////////// 15 | // Defines 16 | ////////////////////////////////////////////////////////////////////////////// 17 | 18 | #define XPT2046_AVG 4 19 | #define CMD_X_READ 0b10010000 20 | #define CMD_Y_READ 0b11010000 21 | 22 | ////////////////////////////////////////////////////////////////////////////// 23 | // Module definition 24 | ////////////////////////////////////////////////////////////////////////////// 25 | 26 | typedef struct _xpt2046_obj_t 27 | { 28 | mp_obj_base_t base; 29 | 30 | uint8_t mhz; 31 | uint8_t spihost; 32 | uint8_t cs; 33 | uint8_t irq; 34 | 35 | int16_t x_min; 36 | int16_t y_min; 37 | int16_t x_max; 38 | int16_t y_max; 39 | bool x_inv; 40 | bool y_inv; 41 | bool xy_swap; 42 | 43 | spi_device_handle_t spi; 44 | int16_t avg_buf_x[XPT2046_AVG]; 45 | int16_t avg_buf_y[XPT2046_AVG]; 46 | uint8_t avg_last; 47 | 48 | } xpt2046_obj_t; 49 | 50 | // Unfortunately, lvgl doesn't pass user_data to callbacks, so we use this global. 51 | // This means we can have only one active touch driver instance, pointed by this global. 52 | STATIC xpt2046_obj_t *g_xpt2046 = NULL; 53 | 54 | STATIC mp_obj_t mp_activate_xpt2046(mp_obj_t self_in) 55 | { 56 | xpt2046_obj_t *self = MP_OBJ_TO_PTR(self_in); 57 | g_xpt2046 = self; 58 | return mp_const_none; 59 | } 60 | 61 | STATIC mp_obj_t xpt2046_make_new(const mp_obj_type_t *type, 62 | size_t n_args, 63 | size_t n_kw, 64 | const mp_obj_t *all_args) 65 | { 66 | enum{ 67 | ARG_mhz, 68 | ARG_spihost, 69 | ARG_cs, 70 | ARG_irq, 71 | 72 | ARG_x_min, 73 | ARG_y_min, 74 | ARG_x_max, 75 | ARG_y_max, 76 | ARG_x_inv, 77 | ARG_y_inv, 78 | ARG_xy_swap, 79 | }; 80 | 81 | static const mp_arg_t allowed_args[] = { 82 | { MP_QSTR_mhz, MP_ARG_INT, {.u_int = 20}}, 83 | { MP_QSTR_spihost, MP_ARG_INT, {.u_int = HSPI_HOST}}, 84 | { MP_QSTR_cs, MP_ARG_INT, {.u_int = 33}}, 85 | { MP_QSTR_irq, MP_ARG_INT, {.u_int = 25}}, 86 | 87 | { MP_QSTR_x_min, MP_ARG_INT, {.u_int = 1000}}, 88 | { MP_QSTR_y_min, MP_ARG_INT, {.u_int = 1000}}, 89 | { MP_QSTR_x_max, MP_ARG_INT, {.u_int = 3200}}, 90 | { MP_QSTR_y_max, MP_ARG_INT, {.u_int = 2000}}, 91 | { MP_QSTR_x_inv, MP_ARG_BOOL, {.u_obj = mp_const_true}}, 92 | { MP_QSTR_y_inv, MP_ARG_BOOL, {.u_obj = mp_const_true}}, 93 | { MP_QSTR_xy_swap, MP_ARG_BOOL, {.u_obj = mp_const_false}}, 94 | }; 95 | 96 | mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; 97 | mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); 98 | xpt2046_obj_t *self = m_new_obj(xpt2046_obj_t); 99 | self->base.type = type; 100 | 101 | self->mhz = args[ARG_mhz].u_int; 102 | self->spihost = args[ARG_spihost].u_int; 103 | self->cs = args[ARG_cs].u_int; 104 | self->irq = args[ARG_irq].u_int; 105 | 106 | self->x_min = args[ARG_x_min].u_int; 107 | self->y_min = args[ARG_y_min].u_int; 108 | self->x_max = args[ARG_x_max].u_int; 109 | self->y_max = args[ARG_y_max].u_int; 110 | self->x_inv = args[ARG_x_inv].u_bool; 111 | self->y_inv = args[ARG_y_inv].u_bool; 112 | self->xy_swap = args[ARG_xy_swap].u_bool; 113 | return MP_OBJ_FROM_PTR(self); 114 | } 115 | 116 | STATIC mp_obj_t mp_xpt2046_init(mp_obj_t self_in); 117 | STATIC mp_obj_t mp_xpt2046_deinit(mp_obj_t self_in); 118 | STATIC void xpt2046_read(lv_indev_t * indev_drv, lv_indev_data_t *data); 119 | 120 | STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_init_xpt2046_obj, mp_xpt2046_init); 121 | STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_deinit_xpt2046_obj, mp_xpt2046_deinit); 122 | STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_activate_xpt2046_obj, mp_activate_xpt2046); 123 | DEFINE_PTR_OBJ(xpt2046_read); 124 | 125 | STATIC const mp_rom_map_elem_t xpt2046_locals_dict_table[] = { 126 | { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&mp_init_xpt2046_obj) }, 127 | { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&mp_deinit_xpt2046_obj) }, 128 | { MP_ROM_QSTR(MP_QSTR_activate), MP_ROM_PTR(&mp_activate_xpt2046_obj) }, 129 | { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&PTR_OBJ(xpt2046_read)) }, 130 | }; 131 | 132 | STATIC MP_DEFINE_CONST_DICT(xpt2046_locals_dict, xpt2046_locals_dict_table); 133 | 134 | STATIC const mp_obj_type_t xpt2046_type = { 135 | { &mp_type_type }, 136 | .name = MP_QSTR_xpt2046, 137 | //.print = xpt2046_print, 138 | .make_new = xpt2046_make_new, 139 | .locals_dict = (mp_obj_dict_t*)&xpt2046_locals_dict, 140 | }; 141 | 142 | STATIC const mp_rom_map_elem_t xpt2046_globals_table[] = { 143 | { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_xpt2046) }, 144 | { MP_ROM_QSTR(MP_QSTR_xpt2046), (mp_obj_t)&xpt2046_type}, 145 | }; 146 | 147 | 148 | STATIC MP_DEFINE_CONST_DICT ( 149 | mp_module_xpt2046_globals, 150 | xpt2046_globals_table 151 | ); 152 | 153 | const mp_obj_module_t mp_module_xpt2046 = { 154 | .base = { &mp_type_module }, 155 | .globals = (mp_obj_dict_t*)&mp_module_xpt2046_globals 156 | }; 157 | 158 | ////////////////////////////////////////////////////////////////////////////// 159 | // Module implementation 160 | ////////////////////////////////////////////////////////////////////////////// 161 | 162 | STATIC mp_obj_t mp_xpt2046_init(mp_obj_t self_in) 163 | { 164 | esp_err_t ret; 165 | 166 | xpt2046_obj_t *self = MP_OBJ_TO_PTR(self_in); 167 | mp_activate_xpt2046(self_in); 168 | 169 | spi_device_interface_config_t devcfg={ 170 | .clock_speed_hz=self->mhz*1000*1000, //Clock out at DISP_SPI_MHZ MHz 171 | .mode=0, //SPI mode 0 172 | .spics_io_num=-1, //CS pin is set manually 173 | .queue_size=1, 174 | .pre_cb=NULL, 175 | .post_cb=NULL, 176 | .flags=SPI_DEVICE_HALFDUPLEX, 177 | .duty_cycle_pos=128, 178 | }; 179 | 180 | 181 | gpio_set_direction(self->irq, GPIO_MODE_INPUT); 182 | gpio_set_direction(self->cs, GPIO_MODE_OUTPUT); 183 | gpio_set_level(self->cs, 1); 184 | 185 | //Attach the touch controller to the SPI bus 186 | ret=spi_bus_add_device(self->spihost, &devcfg, &self->spi); 187 | if (ret != ESP_OK) nlr_raise( 188 | mp_obj_new_exception_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Failed adding SPI device"))); 189 | 190 | return mp_const_none; 191 | } 192 | 193 | STATIC mp_obj_t mp_xpt2046_deinit(mp_obj_t self_in) 194 | { 195 | //xpt2046_obj_t *self = MP_OBJ_TO_PTR(self_in); 196 | 197 | return mp_const_none; 198 | } 199 | 200 | static void xpt2046_corr(xpt2046_obj_t *self, int16_t * x, int16_t * y); 201 | static void xpt2046_avg(xpt2046_obj_t *self, int16_t * x, int16_t * y); 202 | static uint8_t tp_spi_xchg(xpt2046_obj_t *self, uint8_t data_send); 203 | 204 | /** 205 | * Get the current position and state of the touchpad 206 | * @param indev_drv pointer to the caller input device driver 207 | * @param data store the read data here 208 | * @return false: because no more data to be read 209 | */ 210 | static void xpt2046_read(lv_indev_t * indev_drv, lv_indev_data_t * data) 211 | { 212 | xpt2046_obj_t *self = MP_OBJ_TO_PTR(g_xpt2046 ); 213 | if (!self || (!self->spi)) nlr_raise( 214 | mp_obj_new_exception_msg( 215 | &mp_type_RuntimeError, MP_ERROR_TEXT("xpt2046 instance needs to be created before callback is called!"))); 216 | static int16_t last_x = 0; 217 | static int16_t last_y = 0; 218 | bool valid = true; 219 | uint8_t buf; 220 | 221 | int16_t x = 0; 222 | int16_t y = 0; 223 | 224 | uint8_t irq = gpio_get_level(self->irq); 225 | 226 | if(irq == 0) { 227 | gpio_set_level(self->cs, 0); 228 | tp_spi_xchg(self, CMD_X_READ); /*Start x read*/ 229 | 230 | buf = tp_spi_xchg(self, 0); /*Read x MSB*/ 231 | x = buf << 8; 232 | buf = tp_spi_xchg(self, CMD_Y_READ); /*Until x LSB converted y command can be sent*/ 233 | x += buf; 234 | 235 | buf = tp_spi_xchg(self, 0); /*Read y MSB*/ 236 | y = buf << 8; 237 | 238 | buf = tp_spi_xchg(self, 0); /*Read y LSB*/ 239 | y += buf; 240 | gpio_set_level(self->cs, 1); 241 | 242 | /*Normalize Data*/ 243 | x = x >> 3; 244 | y = y >> 3; 245 | xpt2046_corr(self, &x, &y); 246 | xpt2046_avg(self, &x, &y); 247 | last_x = x; 248 | last_y = y; 249 | 250 | 251 | } else { 252 | x = last_x; 253 | y = last_y; 254 | self->avg_last = 0; 255 | valid = false; 256 | } 257 | 258 | data->point.x = x; 259 | data->point.y = y; 260 | data->state = valid == false ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR; 261 | } 262 | 263 | /********************** 264 | * HELPER FUNCTIONS 265 | **********************/ 266 | 267 | static uint8_t tp_spi_xchg(xpt2046_obj_t *self, uint8_t data_send) 268 | { 269 | uint8_t data_rec = 0; 270 | spi_transaction_t t; 271 | memset(&t, 0, sizeof(t)); //Zero out the transaction 272 | t.length = 8; //Length is in bytes, transaction length is in bits. 273 | t.tx_buffer = &data_send; //Data 274 | t.rx_buffer = &data_rec; 275 | 276 | spi_device_queue_trans(self->spi, &t, portMAX_DELAY); 277 | 278 | spi_transaction_t * rt; 279 | spi_device_get_trans_result(self->spi, &rt, portMAX_DELAY); 280 | 281 | return data_rec; 282 | } 283 | 284 | static void xpt2046_corr(xpt2046_obj_t *self, int16_t * x, int16_t * y) 285 | { 286 | if (self->xy_swap){ 287 | int16_t swap_tmp; 288 | swap_tmp = *x; 289 | *x = *y; 290 | *y = swap_tmp; 291 | } 292 | 293 | if((*x) > self->x_min)(*x) -= self->x_min; 294 | else(*x) = 0; 295 | 296 | if((*y) > self->y_min)(*y) -= self->y_min; 297 | else(*y) = 0; 298 | 299 | (*x) = (uint32_t)((uint32_t)(*x) * LV_HOR_RES) / 300 | (self->x_max - self->x_min); 301 | 302 | (*y) = (uint32_t)((uint32_t)(*y) * LV_VER_RES) / 303 | (self->y_max - self->y_min); 304 | 305 | if (self->x_inv){ 306 | (*x) = LV_HOR_RES - (*x); 307 | } 308 | 309 | if (self->y_inv){ 310 | (*y) = LV_VER_RES - (*y); 311 | } 312 | } 313 | 314 | 315 | static void xpt2046_avg(xpt2046_obj_t *self, int16_t * x, int16_t * y) 316 | { 317 | /*Shift out the oldest data*/ 318 | uint8_t i; 319 | for(i = XPT2046_AVG - 1; i > 0 ; i--) { 320 | self->avg_buf_x[i] = self->avg_buf_x[i - 1]; 321 | self->avg_buf_y[i] = self->avg_buf_y[i - 1]; 322 | } 323 | 324 | /*Insert the new point*/ 325 | self->avg_buf_x[0] = *x; 326 | self->avg_buf_y[0] = *y; 327 | if(self->avg_last < XPT2046_AVG) self->avg_last++; 328 | 329 | /*Sum the x and y coordinates*/ 330 | int32_t x_sum = 0; 331 | int32_t y_sum = 0; 332 | for(i = 0; i < self->avg_last ; i++) { 333 | x_sum += self->avg_buf_x[i]; 334 | y_sum += self->avg_buf_y[i]; 335 | } 336 | 337 | /*Normalize the sums*/ 338 | (*x) = (int32_t)x_sum / self->avg_last; 339 | (*y) = (int32_t)y_sum / self->avg_last; 340 | } 341 | -------------------------------------------------------------------------------- /driver/esp32/xpt2046.py: -------------------------------------------------------------------------------- 1 | from machine import Pin 2 | import espidf as esp 3 | import lvgl as lv 4 | 5 | # TODO: Viper/native emmitters don't behave well when module is frozen. 6 | 7 | class xpt2046: 8 | 9 | # Command is 8 bit, but we add another bit as a space before xpt2046 stats sending the response, See Figure 12 on the datasheet 10 | 11 | CMD_X_READ = const(0b100100000) 12 | CMD_Y_READ = const(0b110100000) 13 | CMD_Z1_READ = const(0b101100000) 14 | CMD_Z2_READ = const(0b110000000) 15 | 16 | MAX_RAW_COORD = const((1<<12) - 1) 17 | 18 | def __init__(self, miso=-1, mosi=-1, clk=-1, cs=25, 19 | spihost=esp.HSPI_HOST, half_duplex=True, mhz=5, max_cmds=16, 20 | cal_x0 = 3783, cal_y0 = 3948, cal_x1 = 242, cal_y1 = 423, 21 | transpose = True, samples = 3): 22 | 23 | # Initializations 24 | 25 | if not lv.is_initialized(): 26 | lv.init() 27 | 28 | disp = lv.disp_t() 29 | self.screen_width = disp.get_hor_res() 30 | self.screen_height = disp.get_ver_res() 31 | self.miso = miso 32 | self.mosi = mosi 33 | self.clk = clk 34 | self.cs = cs 35 | self.spihost = spihost 36 | self.half_duplex = half_duplex 37 | self.mhz = mhz 38 | self.max_cmds = max_cmds 39 | self.cal_x0 = cal_x0 40 | self.cal_y0 = cal_y0 41 | self.cal_x1 = cal_x1 42 | self.cal_y1 = cal_y1 43 | self.transpose = transpose 44 | self.samples = samples 45 | 46 | self.touch_count = 0 47 | self.touch_cycles = 0 48 | 49 | self.spi_init() 50 | 51 | self.indev_drv = lv.indev_create() 52 | self.indev_drv.set_type(lv.INDEV_TYPE.POINTER) 53 | self.indev_drv.set_read_cb(self.read) 54 | 55 | def calibrate(self, x0, y0, x1, y1): 56 | self.cal_x0 = x0 57 | self.cal_y0 = y0 58 | self.cal_x1 = x1 59 | self.cal_y1 = y1 60 | 61 | def spi_init(self): 62 | buscfg = esp.spi_bus_config_t({ 63 | "miso_io_num": self.miso, 64 | "mosi_io_num": self.mosi, 65 | "sclk_io_num": self.clk, 66 | "quadwp_io_num": -1, 67 | "quadhd_io_num": -1, 68 | "max_transfer_sz": 4, 69 | }) 70 | 71 | devcfg_flags = 0 # esp.SPI_DEVICE.NO_DUMMY 72 | if self.half_duplex: 73 | devcfg_flags |= esp.SPI_DEVICE.HALFDUPLEX 74 | 75 | devcfg = esp.spi_device_interface_config_t({ 76 | "command_bits": 9, # Actually 8, but need another cycle before xpt starts transmitting response, see Figure 12 on the datasheet. 77 | "clock_speed_hz": self.mhz*1000*1000, 78 | "mode": 0, # SPI mode 0 79 | "spics_io_num": self.cs, # CS pin 80 | "queue_size": self.max_cmds, 81 | "flags": devcfg_flags, 82 | "duty_cycle_pos": 128, 83 | }) 84 | 85 | esp.gpio_pad_select_gpio(self.cs) 86 | 87 | # Initialize the SPI bus, if needed 88 | 89 | if buscfg.miso_io_num >= 0 and \ 90 | buscfg.mosi_io_num >= 0 and \ 91 | buscfg.sclk_io_num >= 0: 92 | 93 | esp.gpio_pad_select_gpio(self.miso) 94 | esp.gpio_pad_select_gpio(self.mosi) 95 | esp.gpio_pad_select_gpio(self.clk) 96 | 97 | esp.gpio_set_direction(self.miso, esp.GPIO_MODE.INPUT) 98 | esp.gpio_set_pull_mode(self.miso, esp.GPIO.PULLUP_ONLY) 99 | esp.gpio_set_direction(self.mosi, esp.GPIO_MODE.OUTPUT) 100 | esp.gpio_set_direction(self.clk, esp.GPIO_MODE.OUTPUT) 101 | 102 | ret = esp.spi_bus_initialize(self.spihost, buscfg, 1) 103 | if ret != 0: raise RuntimeError("Failed initializing SPI bus") 104 | 105 | # Attach the xpt2046 to the SPI bus 106 | 107 | ptr_to_spi = esp.C_Pointer() 108 | ret = esp.spi_bus_add_device(self.spihost, devcfg, ptr_to_spi) 109 | if ret != 0: raise RuntimeError("Failed adding SPI device") 110 | self.spi = ptr_to_spi.ptr_val 111 | 112 | # Prepare transactions. Each response is 16bit long 113 | 114 | self.trans = [esp.spi_transaction_t({ 115 | 'rx_buffer': bytearray(2), 116 | 'length': 0 if self.half_duplex else 16, 117 | 'rxlength': 16 118 | }) for i in range(0, self.max_cmds)] 119 | 120 | trans_result_ptr = esp.C_Pointer() 121 | 122 | # 123 | # Deinitalize SPI device and bus 124 | # 125 | 126 | def deinit(self): 127 | 128 | print('Deinitializing XPT2046...') 129 | 130 | if self.spi: 131 | 132 | # Pop all pending transaction results 133 | ret = 0 134 | while ret == 0: 135 | ret = esp.spi_device_get_trans_result(self.spi, self.trans_result_ptr , 1) 136 | 137 | # Remove device 138 | esp.spi_bus_remove_device(self.spi) 139 | 140 | # Free SPI bus 141 | esp.spi_bus_free(self.spihost) 142 | 143 | 144 | # @micropython.viper 145 | def xpt_cmds(self, cmds): 146 | cmd_count = int(len(cmds)) 147 | for i in range(0, cmd_count): 148 | self.trans[i].cmd = cmds[i] 149 | esp.spi_device_queue_trans(self.spi, self.trans[i], -1) 150 | result = [] 151 | for i in range(0, cmd_count): 152 | esp.spi_device_get_trans_result(self.spi, self.trans_result_ptr , -1) 153 | buf = self.trans[i].rx_buffer.__dereference__(2) 154 | value = (int(buf[0]) << 4) + (int(buf[1]) >> 4) # value is in the 12 higher bits, network order 155 | if value == int(self.MAX_RAW_COORD): 156 | value = 0 157 | result.append(value) 158 | return tuple(result) 159 | 160 | # @micropython.viper 161 | def get_med_coords(self, count : int): 162 | mid = count//2 163 | values = [] 164 | for i in range(0, count): 165 | values.append(self.xpt_cmds([self.CMD_X_READ, self.CMD_Y_READ])) 166 | # values = self.xpt_cmds([self.CMD_X_READ]*count + [self.CMD_Y_READ]*count) 167 | # x_values = sorted(values[:count]) 168 | # y_values = sorted(values[count:]) 169 | x_values = sorted([x for x,y in values]) 170 | y_values = sorted([y for x,y in values]) 171 | if int(x_values[0]) == 0 or int(y_values[0]) == 0 : return None 172 | return x_values[mid], y_values[mid] 173 | 174 | # @micropython.viper 175 | def get_coords(self): 176 | med_coords = self.get_med_coords(int(self.samples)) 177 | if not med_coords: return None 178 | if self.transpose: 179 | raw_y, raw_x = med_coords 180 | else: 181 | raw_x, raw_y = med_coords 182 | 183 | if int(raw_x) != 0 and int(raw_y) != 0: 184 | x = ((int(raw_x) - int(self.cal_x0)) * int(self.screen_width)) // (int(self.cal_x1) - int(self.cal_x0)) 185 | y = ((int(raw_y) - int(self.cal_y0)) * int(self.screen_height)) // (int(self.cal_y1) - int(self.cal_y0)) 186 | # print('(%d, %d) ==> (%d, %d)' % (raw_x, raw_y, x, y)) 187 | return x,y 188 | else: return None 189 | 190 | # @micropython.native 191 | def get_pressure(self, factor : int) -> int: 192 | z1, z2, x = self.xpt_cmds([self.CMD_Z1_READ, self.CMD_Z2_READ, self.CMD_X_READ]) 193 | if int(z1) == 0: return -1 194 | return ( (int(x)*factor) / 4096)*( int(z2)/int(z1) - 1) 195 | 196 | start_time_ptr = esp.C_Pointer() 197 | end_time_ptr = esp.C_Pointer() 198 | cycles_in_ms = esp.esp_clk_cpu_freq() // 1000 199 | 200 | # @micropython.native 201 | def read(self, indev_drv, data) -> int: 202 | 203 | esp.get_ccount(self.start_time_ptr) 204 | coords = self.get_coords() 205 | esp.get_ccount(self.end_time_ptr) 206 | 207 | if self.end_time_ptr.int_val > self.start_time_ptr.int_val: 208 | self.touch_cycles += self.end_time_ptr.int_val - self.start_time_ptr.int_val 209 | self.touch_count += 1 210 | 211 | if coords: 212 | data.point.x ,data.point.y = coords 213 | data.state = lv.INDEV_STATE.PRESSED 214 | return False 215 | data.state = lv.INDEV_STATE.RELEASED 216 | return False 217 | 218 | def stat(self): 219 | return self.touch_cycles / (self.touch_count * self.cycles_in_ms) 220 | 221 | 222 | -------------------------------------------------------------------------------- /driver/generic/axp192.py: -------------------------------------------------------------------------------- 1 | # Python driver for the AXP192 Power Management IC. 2 | # 3 | # https://gist.github.com/ropg/7216ba90a9d7697114d4ba8aea7bee3c 4 | # 5 | # Written in 2021 by Rop Gonggrijp. 6 | # 7 | # Some functionality inspired by C driver written by Mika Tuupola 8 | # (https://github.com/tuupola/axp192) and a fork of that maintained by 9 | # Brian Starkey (https://github.com/usedbytes/axp192) 10 | # 11 | # License: MIT 12 | 13 | 14 | from machine import I2C, Pin 15 | 16 | SDA = 21 17 | SCL = 22 18 | I2C_ADDRESS = 0x34 19 | 20 | # Power control registers 21 | POWER_STATUS = 0x00 22 | CHARGE_STATUS = 0x01 23 | OTG_VBUS_STATUS = 0x04 24 | DATA_BUFFER0 = 0x06 25 | DATA_BUFFER1 = 0x07 26 | DATA_BUFFER2 = 0x08 27 | DATA_BUFFER3 = 0x09 28 | DATA_BUFFER4 = 0x0a 29 | DATA_BUFFER5 = 0x0b 30 | # Output control: 2 EXTEN, 0 DCDC2 31 | EXTEN_DCDC2_CONTROL = 0x10 32 | # Power output control: 6 EXTEN, 4 DCDC2, 3 LDO3, 2 LDO2, 1 DCDC3, 0 DCDC1 33 | DCDC13_LDO23_CONTROL = 0x12 34 | DCDC2_VOLTAGE = 0x23 35 | DCDC2_SLOPE = 0x25 36 | DCDC1_VOLTAGE = 0x26 37 | DCDC3_VOLTAGE = 0x27 38 | # Output voltage control: 7-4 LDO2, 3-0 LDO3 39 | LDO23_VOLTAGE = 0x28 40 | VBUS_IPSOUT_CHANNEL = 0x30 41 | SHUTDOWN_VOLTAGE = 0x31 42 | SHUTDOWN_BATTERY_CHGLED_CONTROL = 0x32 43 | CHARGE_CONTROL_1 = 0x33 44 | CHARGE_CONTROL_2 = 0x34 45 | BATTERY_CHARGE_CONTROL = 0x35 46 | PEK = 0x36 47 | DCDC_FREQUENCY = 0x37 48 | BATTERY_CHARGE_LOW_TEMP = 0x38 49 | BATTERY_CHARGE_HIGH_TEMP = 0x39 50 | APS_LOW_POWER1 = 0x3A 51 | APS_LOW_POWER2 = 0x3B 52 | BATTERY_DISCHARGE_LOW_TEMP = 0x3c 53 | BATTERY_DISCHARGE_HIGH_TEMP = 0x3d 54 | DCDC_MODE = 0x80 55 | ADC_ENABLE_1 = 0x82 56 | ADC_ENABLE_2 = 0x83 57 | ADC_RATE_TS_PIN = 0x84 58 | GPIO30_INPUT_RANGE = 0x85 59 | GPIO0_ADC_IRQ_RISING = 0x86 60 | GPIO0_ADC_IRQ_FALLING = 0x87 61 | TIMER_CONTROL = 0x8a 62 | VBUS_MONITOR = 0x8b 63 | TEMP_SHUTDOWN_CONTROL = 0x8f 64 | 65 | # GPIO control registers 66 | GPIO0_CONTROL = 0x90 67 | GPIO0_LDOIO0_VOLTAGE = 0x91 68 | GPIO1_CONTROL = 0x92 69 | GPIO2_CONTROL = 0x93 70 | GPIO20_SIGNAL_STATUS = 0x94 71 | GPIO40_FUNCTION_CONTROL = 0x95 72 | GPIO40_SIGNAL_STATUS = 0x96 73 | GPIO20_PULLDOWN_CONTROL = 0x97 74 | PWM1_FREQUENCY = 0x98 75 | PWM1_DUTY_CYCLE_1 = 0x99 76 | PWM1_DUTY_CYCLE_2 = 0x9a 77 | PWM2_FREQUENCY = 0x9b 78 | PWM2_DUTY_CYCLE_1 = 0x9c 79 | PWM2_DUTY_CYCLE_2 = 0x9d 80 | N_RSTO_GPIO5_CONTROL = 0x9e 81 | 82 | # Interrupt control registers 83 | ENABLE_CONTROL_1 = 0x40 84 | ENABLE_CONTROL_2 = 0x41 85 | ENABLE_CONTROL_3 = 0x42 86 | ENABLE_CONTROL_4 = 0x43 87 | ENABLE_CONTROL_5 = 0x4a 88 | IRQ_STATUS_1 = 0x44 89 | IRQ_STATUS_2 = 0x45 90 | IRQ_STATUS_3 = 0x46 91 | IRQ_STATUS_4 = 0x47 92 | IRQ_STATUS_5 = 0x4d 93 | 94 | # ADC data registers 95 | ACIN_VOLTAGE = 0x56 96 | ACIN_CURRENT = 0x58 97 | VBUS_VOLTAGE = 0x5a 98 | VBUS_CURRENT = 0x5c 99 | TEMP = 0x5e 100 | TS_INPUT = 0x62 101 | GPIO0_VOLTAGE = 0x64 102 | GPIO1_VOLTAGE = 0x66 103 | GPIO2_VOLTAGE = 0x68 104 | GPIO3_VOLTAGE = 0x6a 105 | BATTERY_POWER = 0x70 106 | BATTERY_VOLTAGE = 0x78 107 | CHARGE_CURRENT = 0x7a 108 | DISCHARGE_CURRENT = 0x7c 109 | APS_VOLTAGE = 0x7e 110 | CHARGE_COULOMB = 0xb0 111 | DISCHARGE_COULOMB = 0xb4 112 | COULOMB_COUNTER_CONTROL = 0xb8 113 | 114 | BIT_DCDC1_ENABLE = 0b00000001 115 | BIT_DCDC2_ENABLE = 0b00010000 116 | BIT_DCDC3_ENABLE = 0b00000010 117 | BIT_LDO2_ENABLE = 0b00000100 118 | BIT_LDO3_ENABLE = 0b00001000 119 | BIT_EXTEN_ENABLE = 0b01000000 120 | 121 | # These are not real registers, see read and write functions 122 | LDO2_VOLTAGE = 0x0101 123 | LDO3_VOLTAGE = 0x0102 124 | 125 | class AXP192(): 126 | 127 | def __init__(self, i2c_dev, sda, scl, freq=400000, i2c_addr=I2C_ADDRESS): 128 | self.i2c = I2C(i2c_dev, sda=Pin(sda), scl=Pin(scl), freq=freq) 129 | self.i2c_addr = i2c_addr 130 | 131 | def read_byte(self, reg_addr): 132 | tmp = self.i2c.readfrom_mem(self.i2c_addr, reg_addr, 1)[0] 133 | # print("read_byte: 0x{:x} from 0x{:x}".format(tmp, reg_addr)) 134 | return tmp 135 | 136 | def write_byte(self, reg_addr, data): 137 | # print("write_byte: 0x{:x} to 0x{:x}".format(data, reg_addr)) 138 | self.i2c.writeto_mem(self.i2c_addr, reg_addr, bytes([data])) 139 | 140 | def twiddle(self, reg_addr, affects, value): 141 | self.write_byte(reg_addr, (self.read_byte(reg_addr) & (affects ^ 0xff)) | (value & affects)) 142 | 143 | def write(self, reg_addr, data): 144 | # print("write: {:x} with {}".format(reg_addr, data)) 145 | if reg_addr == DCDC1_VOLTAGE: 146 | if data == 0: 147 | self.twiddle(DCDC13_LDO23_CONTROL, BIT_DCDC1_ENABLE, 0) 148 | return 149 | if data < 0.7 or data > 3.5: 150 | raise ValueError("Voltage out of range") 151 | self.twiddle(reg_addr, 0b01111111, int((data - 0.7) / 0.025)) 152 | self.twiddle(DCDC13_LDO23_CONTROL, BIT_DCDC1_ENABLE, BIT_DCDC1_ENABLE) 153 | return 154 | elif reg_addr == DCDC2_VOLTAGE: 155 | if data == 0: 156 | self.twiddle(DCDC13_LDO23_CONTROL, BIT_DCDC2_ENABLE, 0) 157 | return 158 | if data < 0.7 or data > 2.275: 159 | raise ValueError("Voltage out of range") 160 | self.twiddle(reg_addr, 0b00111111, int((data - 0.7) / 0.025)) 161 | self.twiddle(DCDC13_LDO23_CONTROL, BIT_DCDC2_ENABLE, BIT_DCDC2_ENABLE) 162 | return 163 | if reg_addr == DCDC3_VOLTAGE: 164 | if data == 0: 165 | self.twiddle(DCDC13_LDO23_CONTROL, BIT_DCDC3_ENABLE, 0) 166 | return 167 | if data < 0.7 or data > 3.5: 168 | raise ValueError("Voltage out of range") 169 | self.twiddle(reg_addr, 0b01111111, int((data - 0.7) / 0.025)) 170 | self.twiddle(DCDC13_LDO23_CONTROL, BIT_DCDC3_ENABLE, BIT_DCDC3_ENABLE) 171 | return 172 | elif reg_addr == LDO2_VOLTAGE: 173 | if data == 0: 174 | self.twiddle(DCDC13_LDO23_CONTROL, BIT_LDO2_ENABLE, 0) 175 | return 176 | if data < 1.8 or data > 3.3: 177 | raise ValueError("Voltage out of range") 178 | val = int((data - 1.8) / 0.1) 179 | self.twiddle(LDO23_VOLTAGE, 0xf0, val << 4) 180 | self.twiddle(DCDC13_LDO23_CONTROL, BIT_LDO2_ENABLE, BIT_LDO2_ENABLE) 181 | return 182 | elif reg_addr == LDO3_VOLTAGE: 183 | if data == 0: 184 | self.twiddle(DCDC13_LDO23_CONTROL, BIT_LDO3_ENABLE, 0) 185 | return 186 | if data < 1.8 or data > 3.3: 187 | raise ValueError("Voltage out of range") 188 | val = int((data - 1.8) / 0.1) 189 | self.twiddle(LDO23_VOLTAGE, 0x0f, val) 190 | self.twiddle(DCDC13_LDO23_CONTROL, BIT_LDO3_ENABLE, BIT_LDO3_ENABLE) 191 | return 192 | 193 | if type(data) != "bytes": 194 | self.write_byte(reg_addr, data) 195 | else: 196 | self.i2c.writeto_mem(self.i2c_addr, reg_addr, data) 197 | 198 | def read(self, reg_addr, length=1): 199 | 200 | if length != 1: 201 | return self.i2c.readfrom_mem(self.i2c_addr, reg_addr, length) 202 | 203 | sensitivity = 1.0 204 | offset = 0.0 205 | 206 | if reg_addr == ACIN_VOLTAGE or reg_addr == VBUS_VOLTAGE: 207 | # 1.7mV per LSB 208 | sensitivity = 1.7 / 1000 209 | elif reg_addr == ACIN_CURRENT: 210 | # 0.625mA per LSB 211 | sensitivity = 0.625 / 1000 212 | elif reg_addr == VBUS_CURRENT: 213 | # 0.375mA per LSB 214 | sensitivity = 0.375 / 1000 215 | elif reg_addr == TEMP: 216 | # 0.1C per LSB, 0x00 = -144.7C 217 | sensitivity = 0.1 218 | offset = -144.7 219 | elif reg_addr == TS_INPUT: 220 | # 0.8mV per LSB 221 | sensitivity = 0.8 / 1000 222 | elif reg_addr == BATTERY_POWER: 223 | # 1.1mV * 0.5mA per LSB 224 | return int.from_bytes(self.read(BATTERY_POWER, 3), "big") * (1.1 * 0.5 / 1000) 225 | elif reg_addr == BATTERY_VOLTAGE: 226 | # 1.1mV per LSB 227 | sensitivity = 1.1 / 1000 228 | elif reg_addr == CHARGE_CURRENT or reg_addr == DISCHARGE_CURRENT: 229 | # 0.5mV per LSB 230 | sensitivity = 0.5 / 1000 231 | elif reg_addr == APS_VOLTAGE: 232 | # 1.4mV per LSB 233 | sensitivity = 1.4 / 1000 234 | elif reg_addr == CHARGE_COULOMB or reg_addr == DISCHARGE_COULOMB: 235 | return int.from_bytes(self.read(reg_addr, 4), "big") 236 | elif reg_addr == DCDC1_VOLTAGE: 237 | if self.read_byte(DCDC13_LDO23_CONTROL) & BIT_DCDC1_ENABLE == 0: 238 | return 0 239 | return (self.read_byte(reg_addr) & 0b01111111) * 0.25 + 0.7 240 | elif reg_addr == DCDC2_VOLTAGE: 241 | if self.read_byte(DCDC13_LDO23_CONTROL) & BIT_DCDC2_ENABLE == 0: 242 | return 0 243 | return (self.read_byte(reg_addr) & 0b00111111) * 0.25 + 0.7 244 | elif reg_addr == DCDC3_VOLTAGE: 245 | if self.read_byte(DCDC13_LDO23_CONTROL) & BIT_DCDC3_ENABLE == 0: 246 | return 0 247 | return (self.read_byte(reg_addr) & 0b01111111) * 0.25 + 0.7 248 | elif reg_addr == LDO2_VOLTAGE: 249 | if self.read_byte(DCDC13_LDO23_CONTROL) & BIT_LDO2_ENABLE == 0: 250 | return 0 251 | return ((self.read_byte(LDO23_VOLTAGE) & 0xf0) >> 4) * 0.1 + 1.8 252 | elif reg_addr == LDO3_VOLTAGE: 253 | if self.read_byte(DCDC13_LDO23_CONTROL) & BIT_LDO3_ENABLE == 0: 254 | return 0 255 | return ((self.read_byte(LDO23_VOLTAGE) & 0x0f)) * 0.1 + 1.8 256 | # any values not listed will just read a single byte 257 | else: 258 | return self.read_byte(reg_addr) 259 | # handle cases above that did not end in return 260 | tmp = self.i2c.readfrom_mem(self.i2c_addr, reg_addr, 2) 261 | return (((tmp[0] << 4) + tmp[1]) * sensitivity) + offset 262 | 263 | def coulomb_counter(self): 264 | # CmAh = 65536 * 0.5mA *(coin - cout) / 3600 / ADC sample rate 265 | return 32768 * (self.read(CHARGE_COULOMB) - self.read(DISCHARGE_COULOMB)) / 3600 / 25 266 | 267 | def coulomb_counter_enable(self): 268 | self.write(COULOMB_COUNTER_CONTROL, 0b10000000) 269 | 270 | def coulomb_counter_disable(self): 271 | self.write(COULOMB_COUNTER_CONTROL, 0b00000000) 272 | 273 | def coulomb_counter_suspend(self): 274 | self.write(COULOMB_COUNTER_CONTROL, 0b11000000) 275 | 276 | def coulomb_counter_clear(self): 277 | self.write(COULOMB_COUNTER_CONTROL, 0b10100000) 278 | -------------------------------------------------------------------------------- /driver/generic/ft6x36.py: -------------------------------------------------------------------------------- 1 | # Pure Python LVGL indev driver for the FocalTech FT6X36 capacitive touch IC 2 | # 3 | # from ft6x36 import ft6x36 4 | # 5 | # touch = ft6x36(sda=, scl=) 6 | # 7 | # If you set the size of the touchpad, you have the option to invert each 8 | # axis, and you'll get some extra robustness against occasional read errors 9 | # as values outside the touchpad are quietly rejected. If you select to swap 10 | # the axes, width and height as well as the inversions refer to the situation 11 | # before the swap. 12 | # 13 | # The nice thing about this driver is that it allows access to the second 14 | # finger, as the FT6X36 is multi-touch. (Two fingers max, with caveats on 15 | # some boards.) 16 | # 17 | # The number of presses is in touch.presses, touch.points[0] and points[1] 18 | # hold the positions. LVGL is not (yet) multi-touch, so all it sees is the 19 | # position in points[0]. 20 | 21 | 22 | import lvgl as lv 23 | from machine import I2C, Pin 24 | 25 | class ft6x36: 26 | 27 | def __init__(self, i2c_dev=0, sda=21, scl=22, freq=400000, addr=0x38, width=-1, height=-1, 28 | inv_x=False, inv_y=False, swap_xy=False): 29 | 30 | if not lv.is_initialized(): 31 | lv.init() 32 | 33 | self.width, self.height = width, height 34 | self.inv_x, self.inv_y, self.swap_xy = inv_x, inv_y, swap_xy 35 | self.i2c = I2C(i2c_dev, sda=Pin(sda), scl=Pin(scl), freq=freq) 36 | self.addr = addr 37 | try: 38 | print("FT6X36 touch IC ready (fw id 0x{0:X} rel {1:d}, lib {2:X})".format( \ 39 | int.from_bytes(self.i2c.readfrom_mem(self.addr, 0xA6, 1), "big"), \ 40 | int.from_bytes(self.i2c.readfrom_mem(self.addr, 0xAF, 1), "big"), \ 41 | int.from_bytes(self.i2c.readfrom_mem(self.addr, 0xA1, 2), "big") \ 42 | )) 43 | except: 44 | print("FT6X36 touch IC not responding") 45 | return 46 | self.point = lv.point_t( {'x': 0, 'y': 0} ) 47 | self.points = [lv.point_t( {'x': 0, 'y': 0} ), lv.point_t( {'x': 0, 'y': 0} )] 48 | self.state = lv.INDEV_STATE.RELEASED 49 | 50 | self.indev_drv = lv.indev_create() 51 | self.indev_drv.set_type(lv.INDEV_TYPE.POINTER) 52 | self.indev_drv.set_read_cb(self.callback) 53 | 54 | def callback(self, driver, data): 55 | 56 | def get_point(offset): 57 | x = (sensorbytes[offset ] << 8 | sensorbytes[offset + 1]) & 0x0fff 58 | y = (sensorbytes[offset + 2] << 8 | sensorbytes[offset + 3]) & 0x0fff 59 | if (self.width != -1 and x >= self.width) or (self.height != -1 and y >= self.height): 60 | raise ValueError 61 | x = self.width - x - 1 if self.inv_x else x 62 | y = self.height - y - 1 if self.inv_y else y 63 | (x, y) = (y, x) if self.swap_xy else (x, y) 64 | return { 'x': x, 'y': y } 65 | 66 | data.point = self.points[0] 67 | data.state = self.state 68 | sensorbytes = self.i2c.readfrom_mem(self.addr, 2, 11) 69 | self.presses = sensorbytes[0] 70 | if self.presses > 2: 71 | return 72 | try: 73 | if self.presses: 74 | self.points[0] = get_point(1) 75 | if self.presses == 2: 76 | self.points[1] = get_point(7) 77 | except ValueError: 78 | return 79 | if sensorbytes[3] >> 4: 80 | self.points[0], self.points[1] = self.points[1], self.points[0] 81 | data.point = self.points[0] 82 | data.state = self.state = lv.INDEV_STATE.PRESSED if self.presses else lv.INDEV_STATE.RELEASED 83 | -------------------------------------------------------------------------------- /driver/generic/ili9xxx-test.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from machine import SPI, Pin 4 | 5 | from ili9xxx import Ili9341_hw 6 | 7 | 8 | def build_rect_buf(w, h, inner=[0x00, 0x00]): 9 | top = b"\xFF\xFF" * w 10 | body = (b"\xFF\xFF\xFF" + bytes(inner) * (w - 3) + b"\xFF\xFF\xFF") * (h - 3) 11 | bot = b"\xFF\xFF" * w 12 | return top + body + bot 13 | 14 | 15 | spi = SPI( 16 | 0, 17 | baudrate=24_000_000, 18 | sck=Pin(18), 19 | mosi=Pin(19), 20 | miso=Pin(16), 21 | ) 22 | lcd = Ili9341_hw(spi=spi, cs=17, dc=15, rst=14) 23 | 24 | lcd.set_backlight(30) 25 | for rot in (0, 1, 2, 3): 26 | lcd.apply_rotation(rot) 27 | 28 | lcd.clear(0x0000) 29 | # 1/4 screen pixels square with white border red background 30 | w, h = lcd.width // 4, lcd.height // 8 31 | bmp = build_rect_buf(w, h, [0x03, 0x03]) 32 | t0 = time.ticks_us() 33 | lcd.blit(w, h, w, h, bmp) 34 | t1 = time.ticks_us() 35 | bmp = build_rect_buf(lcd.width, lcd.height // 20, [0x09, 0x09]) 36 | lcd.blit(0, 0, lcd.width, lcd.height // 20, bmp) 37 | 38 | print("Maximum FPS @24MHz:", 24e6 / (320 * 240 * 16)) # FPS = F/(W*H*BPP) 39 | print( 40 | "Achieved FPS:", 1 / (16 * (t1 - t0) * 1e-6) 41 | ) # Note: Test only draws 1/16 of the sreen area 42 | 43 | print("Draw TSC calibration pattern") 44 | w, h, wu, hu = lcd.width // 10, lcd.height // 10, lcd.width // 5, lcd.height // 5 45 | bmp = build_rect_buf(w, h, [0xA0, 0xF0]) 46 | lcd.blit(wu, hu, w, h, bmp) 47 | lcd.blit(4 * wu, hu, w, h, bmp) 48 | lcd.blit(4 * wu, 4 * hu, w, h, bmp) 49 | lcd.blit(wu, 4 * hu, w, h, bmp) 50 | time.sleep(0.5) 51 | -------------------------------------------------------------------------------- /driver/generic/ili9xxx.py: -------------------------------------------------------------------------------- 1 | """Generic ILI9xxx drivers. 2 | 3 | This code is licensed under MIT license. 4 | 5 | Adapted from: 6 | https://github.com/rdagger/micropython-ili9341 7 | 8 | The following code snippet will instantiate the driver and 9 | automatically register it to lvgl. Adjust the SPI bus and 10 | pin configurations to match your hardware setup:: 11 | 12 | import ili9xxx 13 | from machine import SPI, Pin 14 | spi = SPI(0, baudrate=24_000_000, sck=Pin(18), mosi=Pin(19), miso=Pin(16)) 15 | drv = ili9xxx.Ili9341(spi=spi, dc=15, cs=17, rst=14) 16 | """ 17 | from micropython import const 18 | 19 | import st77xx 20 | 21 | # Command constants from ILI9341 datasheet 22 | _NOP = const(0x00) # No-op 23 | _SWRESET = const(0x01) # Software reset 24 | _RDDID = const(0x04) # Read display ID info 25 | _RDDST = const(0x09) # Read display status 26 | _SLPIN = const(0x10) # Enter sleep mode 27 | _SLPOUT = const(0x11) # Exit sleep mode 28 | _PTLON = const(0x12) # Partial mode on 29 | _NORON = const(0x13) # Normal display mode on 30 | _RDMODE = const(0x0A) # Read display power mode 31 | _RDMADCTL = const(0x0B) # Read display MADCTL 32 | _RDPIXFMT = const(0x0C) # Read display pixel format 33 | _RDIMGFMT = const(0x0D) # Read display image format 34 | _RDSELFDIAG = const(0x0F) # Read display self-diagnostic 35 | _INVOFF = const(0x20) # Display inversion off 36 | _INVON = const(0x21) # Display inversion on 37 | _GAMMASET = const(0x26) # Gamma set 38 | _DISPLAY_OFF = const(0x28) # Display off 39 | _DISPLAY_ON = const(0x29) # Display on 40 | _SET_COLUMN = const(0x2A) # Column address set 41 | _SET_PAGE = const(0x2B) # Page address set 42 | _WRITE_RAM = const(0x2C) # Memory write 43 | _READ_RAM = const(0x2E) # Memory read 44 | _PTLAR = const(0x30) # Partial area 45 | _VSCRDEF = const(0x33) # Vertical scrolling definition 46 | _MADCTL = const(0x36) # Memory access control 47 | _VSCRSADD = const(0x37) # Vertical scrolling start address 48 | _PIXFMT = const(0x3A) # COLMOD: Pixel format set 49 | _WRITE_DISPLAY_BRIGHTNESS = const(0x51) # Brightness hardware dependent! 50 | _READ_DISPLAY_BRIGHTNESS = const(0x52) 51 | _WRITE_CTRL_DISPLAY = const(0x53) 52 | _READ_CTRL_DISPLAY = const(0x54) 53 | _WRITE_CABC = const(0x55) # Write Content Adaptive Brightness Control 54 | _READ_CABC = const(0x56) # Read Content Adaptive Brightness Control 55 | _WRITE_CABC_MINIMUM = const(0x5E) # Write CABC Minimum Brightness 56 | _READ_CABC_MINIMUM = const(0x5F) # Read CABC Minimum Brightness 57 | _FRMCTR1 = const(0xB1) # Frame rate control (In normal mode/full colors) 58 | _FRMCTR2 = const(0xB2) # Frame rate control (In idle mode/8 colors) 59 | _FRMCTR3 = const(0xB3) # Frame rate control (In partial mode/full colors) 60 | _INVCTR = const(0xB4) # Display inversion control 61 | _DFUNCTR = const(0xB6) # Display function control 62 | _PWCTR1 = const(0xC0) # Power control 1 63 | _PWCTR2 = const(0xC1) # Power control 2 64 | _PWCTRA = const(0xCB) # Power control A 65 | _PWCTRB = const(0xCF) # Power control B 66 | _VMCTR1 = const(0xC5) # VCOM control 1 67 | _VMCTR2 = const(0xC7) # VCOM control 2 68 | _RDID1 = const(0xDA) # Read ID 1 69 | _RDID2 = const(0xDB) # Read ID 2 70 | _RDID3 = const(0xDC) # Read ID 3 71 | _RDID4 = const(0xDD) # Read ID 4 72 | _GMCTRP1 = const(0xE0) # Positive gamma correction 73 | _GMCTRN1 = const(0xE1) # Negative gamma correction 74 | _DTCA = const(0xE8) # Driver timing control A 75 | _DTCB = const(0xEA) # Driver timing control B 76 | _POSC = const(0xED) # Power on sequence control 77 | _ENABLE3G = const(0xF2) # Enable 3 gamma control 78 | _PUMPRC = const(0xF7) # Pump ratio control 79 | 80 | _MADCTL_MY = const(0x80) # page address order (0: top to bottom; 1: bottom to top) 81 | _MADCTL_MX = const(0x40) # column address order (0: left to right; 1: right to left) 82 | _MADCTL_MV = const(0x20) # page/column order (0: normal mode 1; reverse mode) 83 | _MADCTL_ML = const( 84 | 0x10 85 | ) # line address order (0: refresh to to bottom; 1: refresh bottom to top) 86 | _MADCTL_BGR = const(0x08) # colors are BGR (not RGB) 87 | _MADCTL_RTL = const(0x04) # refresh right to left 88 | 89 | _MADCTL_ROTS = ( 90 | const(_MADCTL_MX), # 0 = portrait 91 | const(_MADCTL_MV), # 1 = landscape 92 | const(_MADCTL_MY), # 2 = inverted portrait 93 | const(_MADCTL_MX | _MADCTL_MY | _MADCTL_MV), # 3 = inverted landscape 94 | ) 95 | 96 | ILI9XXX_PORTRAIT = st77xx.ST77XX_PORTRAIT 97 | ILI9XXX_LANDSCAPE = st77xx.ST77XX_LANDSCAPE 98 | ILI9XXX_INV_PORTRAIT = st77xx.ST77XX_INV_PORTRAIT 99 | ILI9XXX_INV_LANDSCAPE = st77xx.ST77XX_INV_LANDSCAPE 100 | 101 | 102 | class Ili9341_hw(st77xx.St77xx_hw): 103 | def __init__(self, **kw): 104 | """ILI9341 TFT Display Driver. 105 | 106 | Requires ``LV_COLOR_DEPTH=16`` when building lv_micropython to function. 107 | """ 108 | super().__init__( 109 | res=(240, 320), 110 | suppRes=[ 111 | (240, 320), 112 | ], 113 | model=None, 114 | suppModel=None, 115 | bgr=False, 116 | **kw, 117 | ) 118 | 119 | def config_hw(self): 120 | self._run_seq( 121 | [ 122 | (_SLPOUT, None, 100), 123 | (_PWCTRB, b"\x00\xC1\x30"), # Pwr ctrl B 124 | (_POSC, b"\x64\x03\x12\x81"), # Pwr on seq. ctrl 125 | (_DTCA, b"\x85\x00\x78"), # Driver timing ctrl A 126 | (_PWCTRA, b"\x39\x2C\x00\x34\x02"), # Pwr ctrl A 127 | (_PUMPRC, b"\x20"), # Pump ratio control 128 | (_DTCB, b"\x00\x00"), # Driver timing ctrl B 129 | (_PWCTR1, b"\x23"), # Pwr ctrl 1 130 | (_PWCTR2, b"\x10"), # Pwr ctrl 2 131 | (_VMCTR1, b"\x3E\x28"), # VCOM ctrl 1 132 | (_VMCTR2, b"\x86"), # VCOM ctrl 2 133 | (_VSCRSADD, b"\x00"), # Vertical scrolling start address 134 | (_PIXFMT, b"\x55"), # COLMOD: Pixel format 135 | (_FRMCTR1, b"\x00\x18"), # Frame rate ctrl 136 | (_DFUNCTR, b"\x08\x82\x27"), 137 | (_ENABLE3G, b"\x00"), # Enable 3 gamma ctrl 138 | (_GAMMASET, b"\x01"), # Gamma curve selected 139 | ( 140 | _GMCTRP1, 141 | b"\x0F\x31\x2B\x0C\x0E\x08\x4E\xF1\x37\x07\x10\x03\x0E\x09\x00", 142 | ), 143 | ( 144 | _GMCTRN1, 145 | b"\x00\x0E\x14\x03\x11\x07\x31\xC1\x48\x08\x0F\x0C\x31\x36\x0F", 146 | ), 147 | (_SLPOUT, None, 100), 148 | (_DISPLAY_ON, None), 149 | ] 150 | ) 151 | 152 | def apply_rotation(self, rot): 153 | self.rot = rot 154 | if (self.rot % 2) == 0: 155 | self.width, self.height = self.res 156 | else: 157 | self.height, self.width = self.res 158 | self.write_register( 159 | _MADCTL, 160 | bytes([_MADCTL_BGR | _MADCTL_ROTS[self.rot % 4]]), 161 | ) 162 | 163 | 164 | class Ili9341(Ili9341_hw, st77xx.St77xx_lvgl): 165 | def __init__(self, doublebuffer=True, factor=4, **kw): 166 | """See :obj:`Ili9341_hw` for the meaning of the parameters.""" 167 | import lvgl as lv 168 | 169 | Ili9341_hw.__init__(self, **kw) 170 | st77xx.St77xx_lvgl.__init__(self, doublebuffer, factor) 171 | -------------------------------------------------------------------------------- /driver/generic/st77xx-test.py: -------------------------------------------------------------------------------- 1 | import machine 2 | import sys 3 | sys.path.append('.') 4 | from st77xx import * 5 | 6 | def build_rect_buf(w, h, inner=[0x00,0x00]): 7 | top = b"\xFF\xFF"*w 8 | body=(b"\xFF\xFF\xFF" + bytes(inner)*(w-3) + b"\xFF\xFF\xFF")*(h-3) 9 | bot = b"\xFF\xFF"*w 10 | return top + body + bot 11 | 12 | def test_lcd(lcd): 13 | # lcd.hard_reset() 14 | lcd.set_backlight(30) 15 | for rot in (0,1,2,3): 16 | lcd.apply_rotation(rot) 17 | 18 | lcd.clear(0x0000) 19 | # 1/4 screen pixels square with white border red background 20 | w,h=lcd.width//4,lcd.height//8 21 | bmp=build_rect_buf(w,h,[0x03,0x03]) 22 | t0=time.ticks_us() 23 | lcd.blit(w,h,w,h,bmp) 24 | t1=time.ticks_us() 25 | bmp=build_rect_buf(lcd.width,lcd.height//20,[0x09,0x09]) 26 | lcd.blit(0,0,lcd.width,lcd.height//20,bmp) 27 | 28 | print("Maximum FPS @24MHz:",24e6/(320*240*16)) # FPS = F/(W*H*BPP) 29 | print("Achieved FPS:",1/(16*(t1-t0)*1e-6)) # Note: Test only draws 1/16 of the sreen area 30 | 31 | print( "Draw TSC calibration pattern") 32 | w,h,wu,hu=lcd.width//10,lcd.height//10,lcd.width//5,lcd.height//5 33 | bmp=build_rect_buf(w,h,[0xa0,0xf0]) 34 | lcd.blit(wu,hu,w,h,bmp) 35 | lcd.blit(4*wu,hu,w,h,bmp) 36 | lcd.blit(4*wu,4*hu,w,h,bmp) 37 | lcd.blit(wu,4*hu,w,h,bmp) 38 | time.sleep(.5) 39 | 40 | for p in (20,100,80,50,10,60): 41 | lcd.set_backlight(p) 42 | time.sleep(.1) 43 | 44 | spi=machine.SPI( 45 | 1, 46 | baudrate=24_000_000, 47 | polarity=0, 48 | phase=0, 49 | sck=machine.Pin(10,machine.Pin.OUT), 50 | mosi=machine.Pin(11,machine.Pin.OUT), 51 | miso=machine.Pin(12,machine.Pin.IN) 52 | ) 53 | # dma=rp2_dma.DMA(0) 54 | rp2_dma=None 55 | 56 | if 1: 57 | # Waveshare Pi Pico 2.8 LCD https://www.waveshare.com/Pico-ResTouch-LCD-2.8.htm 58 | waveshare_28_lcd=St7789_hw(rot=0,res=(240,320),spi=spi,rp2_dma=rp2_dma,cs=9,dc=8,bl=13,rst=15) 59 | test_lcd(lcd=waveshare_28_lcd) 60 | if 1: 61 | # Waveshare Pi Pico 1.8 LCD https://www.waveshare.com/wiki/Pico-LCD-1.8 62 | # (not sure if this is redtab, but the driver works; someone with access to more hardware can adjust perhaps) 63 | waveshare_18_lcd=St7735_hw(rot=0,res=(128,160),spi=spi,rp2_dma=rp2_dma,cs=9,dc=8,bl=13,rst=12,model='redtab') 64 | test_lcd(lcd=waveshare_18_lcd) 65 | if 1: 66 | # no-name variant which arduino library calls blacktab (IIRC) 67 | noname_177_lcd=St7735_hw(rot=0,res=(128,160),spi=spi,rp2_dma=None,cs=9,dc=8,bl=13,rst=12,model='blacktab') 68 | test_lcd(lcd=noname_177_lcd) 69 | 70 | -------------------------------------------------------------------------------- /driver/generic/xpt2046-test.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append('.') 3 | from xpt2046 import * 4 | 5 | 6 | TP_CLK_PIN,TP_MOSI_PIN,TP_MISO_PIN=10,11,12 7 | TP_CS_PIN=16 8 | spi=machine.SPI( 9 | 1, 10 | baudrate=2_000_000, # the chip does not handle more than 2MHz (!) 11 | polarity=0, 12 | phase=0, 13 | sck=machine.Pin(TP_CLK_PIN, machine.Pin.OUT), 14 | mosi=machine.Pin(TP_MOSI_PIN, machine.Pin.OUT), 15 | miso=machine.Pin(TP_MISO_PIN, machine.Pin.OUT), 16 | ) 17 | 18 | tsc=Xpt2046_hw(spi=spi,cs=TP_CS_PIN,rot=1) 19 | for i in range(100000): 20 | if p:=tsc.pos(): print(p) 21 | 22 | -------------------------------------------------------------------------------- /driver/generic/xpt2046.py: -------------------------------------------------------------------------------- 1 | # © 2022 Václav Šmilauer 2 | # MIT-licensed 3 | import machine 4 | import struct 5 | 6 | # for inspiration see e.g. 7 | # https://github.com/MatthewLowden/RPi-XPT2046-Touchscreen-Python/blob/master/XPT2046.py 8 | # and the XPT2046 datasheet 9 | 10 | XPT2046_PORTRAIT = const(0) 11 | XPT2046_LANDSCAPE = const(1) 12 | XPT2046_INV_PORTRAIT = const(2) 13 | XPT2046_INV_LANDSCAPE = const(3) 14 | 15 | 16 | class Xpt2046_hw(object): 17 | CHAN_X = const(0b0101_0000) 18 | CHAN_Y = const(0b0001_0000) 19 | CHAN_Z1 = const(0b0011_0000) 20 | CHAN_Z2 = const(0b0100_0000) 21 | CHAN_T0 = const(0b0000_0000) 22 | CHAN_T1 = const(0b0111_0000) 23 | CHAN_BAT= const(0b0010_0000) 24 | CHAN_AUX= const(0b0110_0000) 25 | 26 | CONV_8_BIT =const(0b0000_1000) 27 | CONV_12_BIT=const(0b0000_0000) 28 | START_BIT =const(0b1000_0000) 29 | 30 | def _chanRead(self,chan): 31 | self.cs.value(0) 32 | struct.pack_into('BBB',self.buf,0,Xpt2046.START_BIT|self.conv|chan,0,0) 33 | self.spi.write_readinto(self.buf,self.buf) 34 | if self.conv==Xpt2046.CONV_8_BIT: ret=self.buf[1] 35 | else: ret=(self.buf[1]<<4)|(self.buf[2]>>4) 36 | self.cs.value(1) 37 | return ret 38 | 39 | def __init__(self,*, 40 | spi: machine.SPI,cs,bits=12,ranges=((100,1900),(200,1950)),width=240,height=320,rot=XPT2046_PORTRAIT): 41 | ''' 42 | Construct the Xpt2046 touchscreen controller. 43 | *spi*: spi bus instance; its baud rate must *not* exceed 2_000_000 (2MHz) for correct functionality 44 | *cs*: chip select (GPIO number or machine.Pin instance) 45 | *bits*: ADC precision, can be 12 or 8; note that 8 will require you to provide different *ranges* 46 | *ranges*: `(x_min,x_max),(y_min,y_max)` for raw coordinate readings; calibrated values might be provided. 47 | *width*: width of the underlying screen in pixels, in natural (rot=0) orientation (0..*width* is the range for reported horizontal coordinate) 48 | *height*: height of the underlying screen in pixels 49 | *rot*: screen rotation (0: portrait, 1: landscape, 2: inverted portrait, 3: inverted landscape); the constants XPT2046_PORTRAIT, XPT2046_LANDSCAPE, XPT2046_INV_PORTRAIT, XPT2046_INV_LANDSCAPE may be used. 50 | ''' 51 | self.buf = bytearray(3) 52 | self.spi = spi 53 | self.cs = (machine.Pin(cs,machine.Pin.OUT) if isinstance(cs,int) else cs) 54 | self.cs.value(1) 55 | if bits not in (8,12): raise ValueError('Xpt2046.bits: must be 8 or 12 (not %s)'%str(bits)) 56 | self.conv=(Xpt2046.CONV_8_BIT if bits==8 else Xpt2046.CONV_12_BIT) 57 | self.xy_range,self.dim,self.rot=ranges,(width,height),(rot%4) 58 | self.xy_scale=[self.dim[ax]*1./(self.xy_range[ax][1]-self.xy_range[ax][0]) for ax in (0,1)] 59 | self.xy_origin=[self.xy_range[ax][0] for ax in (0,1)] 60 | def _raw2px(self,rxy): 61 | 'Convert raw coordinates to pixel coordinates' 62 | x,y=[int(self.xy_scale[ax]*(rxy[ax]-self.xy_origin[ax])) for ax in (0,1)] 63 | if self.rot==0: return self.dim[0]-x,y 64 | elif self.rot==1: return self.dim[1]-y,x 65 | elif self.rot==2: return x,self.dim[1]-y 66 | else: return y,self.dim[0]-x 67 | def _raw_pos(self): 68 | 'Read raw position; return value if within valid ranges (`__init__(ranges=...)`) or `None` if outside.' 69 | ret=[0,0] 70 | for ax,chan in [(0,Xpt2046.CHAN_X),(1,Xpt2046.CHAN_Y)]: 71 | r=self._chanRead(chan) 72 | if not self.xy_range[ax][0]<=r<=self.xy_range[ax][1]: return None 73 | ret[ax]=r 74 | return ret 75 | 76 | def pos(self,N=10,attempts=20): 77 | '''' 78 | Get N position readings (limited by 20 attempts) and return mean position of valid readings. 79 | If attempts are exhausted, return None. 80 | ''' 81 | N,attempts=10,20 82 | xx,yy,done=0,0,0 83 | for _ in range(attempts): 84 | if (r:=self._raw_pos()) is None: continue 85 | xx+=r[0]; yy+=r[1]; done+=1 86 | if done==N: break 87 | else: return None 88 | mx,my=xx*1./N,yy*1./N 89 | return self._raw2px((mx,my)) 90 | 91 | 92 | class Xpt2046(Xpt2046_hw): 93 | def indev_drv_read_cb(self, indev_drv, data): 94 | # wait for DMA transfer (if any) before switchint SPI to 1 MHz 95 | if self.spiPrereadCb: self.spiPrereadCb() 96 | # print('.',end='') 97 | if self.spiRate: self.spi.init(baudrate=1_000_000) 98 | pos=self.pos() 99 | if pos is None: data.state=0 100 | else: (data.point.x,data.point.y),data.state=pos,1 101 | # print('#',end='') 102 | # switch SPI back to spiRate 103 | if self.spiRate: self.spi.init(baudrate=self.spiRate) 104 | 105 | def __init__(self,spi,spiRate=24_000_000,spiPrereadCb=None,**kw): 106 | '''XPT2046 touchscreen driver for LVGL; cf. documentation of :obj:`Xpt2046_hw` for the meaning of parameters being passed. 107 | 108 | *spiPrereadCb*: call this before reading from SPI; used to block until DMA transfer is complete (when sharing SPI bus). 109 | *spiRate*: the SPI bus must set to low frequency (1MHz) when reading from the XPT2046; when *spiRate* is given, the bus will be switched back to this frequency when XPT2046 is done reading. The default 24MHz targets St77xx display chips which operate at that frequency and come often with XPT2046-based touchscreen. 110 | ''' 111 | super().__init__(spi=spi,**kw) 112 | self.spiRate=spiRate 113 | self.spiPrereadCb=spiPrereadCb 114 | 115 | import lvgl as lv 116 | if not lv.is_initialized(): lv.init() 117 | 118 | self.indev_drv = lv.indev_create() 119 | self.indev_drv.set_type(lv.INDEV_TYPE.POINTER) 120 | self.indev_drv.set_read_cb(self.indev_drv_read_cb) 121 | -------------------------------------------------------------------------------- /driver/include/common.h: -------------------------------------------------------------------------------- 1 | #ifndef __LVMP_DRV_COMMON_H 2 | #define __LVMP_DRV_COMMON_H 3 | 4 | #include "py/obj.h" 5 | #include "py/runtime.h" 6 | #include "py/binary.h" 7 | 8 | ////////////////////////////////////////////////////////////////////////////// 9 | // A read-only buffer that contains a C pointer 10 | // Used to communicate function pointers to lvgl Micropython bindings 11 | // 12 | 13 | typedef struct mp_ptr_t 14 | { 15 | mp_obj_base_t base; 16 | void *ptr; 17 | } mp_ptr_t; 18 | 19 | STATIC mp_int_t mp_ptr_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) 20 | { 21 | mp_ptr_t *self = MP_OBJ_TO_PTR(self_in); 22 | 23 | if (flags & MP_BUFFER_WRITE) { 24 | // read-only ptr 25 | return 1; 26 | } 27 | 28 | bufinfo->buf = &self->ptr; 29 | bufinfo->len = sizeof(self->ptr); 30 | bufinfo->typecode = BYTEARRAY_TYPECODE; 31 | return 0; 32 | } 33 | 34 | #define PTR_OBJ(ptr_global) ptr_global ## _obj 35 | 36 | #define DEFINE_PTR_OBJ_TYPE(ptr_obj_type, ptr_type_qstr)\ 37 | STATIC MP_DEFINE_CONST_OBJ_TYPE(\ 38 | ptr_obj_type,\ 39 | ptr_type_qstr,\ 40 | MP_TYPE_FLAG_NONE,\ 41 | buffer, mp_ptr_get_buffer\ 42 | ); 43 | 44 | #define DEFINE_PTR_OBJ(ptr_global)\ 45 | DEFINE_PTR_OBJ_TYPE(ptr_global ## _type, MP_QSTR_ ## ptr_global);\ 46 | STATIC const mp_ptr_t PTR_OBJ(ptr_global) = {\ 47 | { &ptr_global ## _type },\ 48 | &ptr_global\ 49 | } 50 | 51 | #define NEW_PTR_OBJ(name, value)\ 52 | ({\ 53 | DEFINE_PTR_OBJ_TYPE(ptr_obj_type, MP_QSTR_ ## name);\ 54 | mp_ptr_t *self = m_new_obj(mp_ptr_t);\ 55 | self->base.type = &ptr_obj_type;\ 56 | self->ptr = value;\ 57 | MP_OBJ_FROM_PTR(self);\ 58 | }) 59 | 60 | #endif // __LVMP_DRV_COMMON_H 61 | -------------------------------------------------------------------------------- /driver/js/lv_timer.py: -------------------------------------------------------------------------------- 1 | # Timer implementation for the JS port. 2 | # 3 | # Idea by X-Ryl669: https://github.com/lvgl/lvgl/issues/4011#issuecomment-1483094051 4 | 5 | import jswindow 6 | class Timer: 7 | PERIODIC = 0 8 | ONE_SHOT = 1 9 | 10 | def __init__(self, id): 11 | pass 12 | 13 | def init(self, mode=PERIODIC, period=-1, callback=None): 14 | self.mode = mode 15 | if mode == Timer.PERIODIC: 16 | self.id = jswindow.setInterval(callback, period) 17 | else: 18 | self.id = jswindow.setTimeout(callback, period) 19 | 20 | def deinit(self): 21 | if self.mode == Timer.PERIODIC: 22 | jswindow.clearInterval(self.id) 23 | else: 24 | jswindow.clearTimeout(self.id) 25 | -------------------------------------------------------------------------------- /driver/linux/evdev.py: -------------------------------------------------------------------------------- 1 | # LVGL indev driver for evdev mouse device 2 | # (for the unix micropython port) 3 | 4 | import ustruct 5 | import select 6 | import lvgl as lv 7 | 8 | # Default crosshair cursor 9 | class crosshair_cursor: 10 | def __init__(self, scr=None): 11 | self.scr = scr if scr else lv.scr_act() 12 | self.hor_res = self.scr.get_width() 13 | self.ver_res = self.scr.get_height() 14 | self.cursor_style = lv.style_t() 15 | self.cursor_style.set_line_width(1) 16 | self.cursor_style.set_line_dash_gap(5) 17 | self.cursor_style.set_line_dash_width(1) 18 | self.cursor_hor = lv.line(self.scr) 19 | self.cursor_hor.add_style(self.cursor_style, lv.PART.MAIN) 20 | self.cursor_ver = lv.line(self.scr) 21 | self.cursor_ver.add_style(self.cursor_style, lv.PART.MAIN) 22 | 23 | def __call__(self, data): 24 | # print("%d : %d:%d" % (data.state, data.point.x, data.point.y)) 25 | self.cursor_hor.set_points([{'x':0,'y':data.point.y},{'x':self.hor_res,'y':data.point.y}],2) 26 | self.cursor_ver.set_points([{'y':0,'x':data.point.x},{'y':self.ver_res,'x':data.point.x}],2) 27 | 28 | def delete(self): 29 | self.cursor_hor.delete() 30 | self.cursor_ver.delete() 31 | 32 | # evdev driver for mouse 33 | class mouse_indev: 34 | def __init__(self, scr=None, cursor=None, device='/dev/input/mice'): 35 | 36 | # Open evdev and initialize members 37 | self.evdev = open(device, 'rb') 38 | self.poll = select.poll() 39 | self.poll.register(self.evdev.fileno()) 40 | self.scr = scr if scr else lv.scr_act() 41 | self.cursor = cursor if cursor else crosshair_cursor(self.scr) 42 | self.hor_res = self.scr.get_width() 43 | self.ver_res = self.scr.get_height() 44 | 45 | # Register LVGL indev driver 46 | self.indev = lv.indev_create() 47 | self.indev.set_type(lv.INDEV_TYPE.POINTER) 48 | self.indev.set_read_cb(self.mouse_read) 49 | 50 | def mouse_read(self, indev, data) -> int: 51 | 52 | # Check if there is input to be read from evdev 53 | if not self.poll.poll()[0][1] & select.POLLIN: 54 | return 0 55 | 56 | # Read and parse evdev mouse data 57 | mouse_data = ustruct.unpack('bbb',self.evdev.read(3)) 58 | 59 | # Data is relative, update coordinates 60 | data.point.x += mouse_data[1] 61 | data.point.y -= mouse_data[2] 62 | 63 | # Handle coordinate overflow cases 64 | data.point.x = min(data.point.x, self.hor_res - 1) 65 | data.point.y = min(data.point.y, self.ver_res - 1) 66 | data.point.x = max(data.point.x, 0) 67 | data.point.y = max(data.point.y, 0) 68 | 69 | # Update "pressed" status 70 | data.state = lv.INDEV_STATE.PRESSED if ((mouse_data[0] & 1) == 1) else lv.INDEV_STATE.RELEASED 71 | 72 | # Draw cursor, if needed 73 | if self.cursor: self.cursor(data) 74 | return 0 75 | 76 | def delete(self): 77 | self.evdev.close() 78 | if self.cursor and hasattr(self.cursor, 'delete'): 79 | self.cursor.delete() 80 | self.indev.enable(False) 81 | -------------------------------------------------------------------------------- /driver/linux/lv_timer.py: -------------------------------------------------------------------------------- 1 | 2 | # Timer that matches machine.Timer (https://docs.micropython.org/en/latest/library/machine.Timer.html) 3 | # for the unix port. 4 | # 5 | # MIT license; Copyright (c) 2021 Amir Gonnen 6 | # 7 | # Based on timer.py from micropython-lib (https://github.com/micropython/micropython-lib/blob/master/unix-ffi/machine/machine/timer.py) 8 | 9 | 10 | import ffi 11 | import uctypes 12 | import array 13 | import os 14 | import sys 15 | 16 | # FFI libraries 17 | 18 | libc = ffi.open("libc.so.6") 19 | try: 20 | librt = ffi.open("librt.so") 21 | except OSError as e: 22 | librt = libc 23 | 24 | 25 | # C constants 26 | 27 | CLOCK_REALTIME = 0 28 | CLOCK_MONOTONIC = 1 29 | SIGEV_SIGNAL = 0 30 | 31 | # C structs 32 | 33 | sigaction_t = { 34 | "sa_handler" : (0 | uctypes.UINT64), 35 | "sa_mask" : (8 | uctypes.ARRAY, 16 | uctypes.UINT64), 36 | "sa_flags" : (136 | uctypes.INT32), 37 | "sa_restorer": (144 |uctypes.PTR, uctypes.UINT8), 38 | } 39 | 40 | sigval_t = { 41 | "sival_int": 0 | uctypes.INT32, 42 | "sival_ptr": (0 | uctypes.PTR, uctypes.UINT8), 43 | } 44 | 45 | sigevent_t = { 46 | "sigev_value": (0, sigval_t), 47 | "sigev_signo": uctypes.sizeof(sigval_t) | uctypes.INT32, 48 | "sigev_notify": (uctypes.sizeof(sigval_t) + 4) | uctypes.INT32, 49 | } 50 | 51 | timespec_t = { 52 | "tv_sec": 0 | uctypes.INT32, 53 | "tv_nsec": 8 | uctypes.INT64, 54 | } 55 | 56 | itimerspec_t = { 57 | "it_interval": (0, timespec_t), 58 | "it_value": (16, timespec_t), 59 | } 60 | 61 | # C functions 62 | 63 | __libc_current_sigrtmin = libc.func("i", "__libc_current_sigrtmin", "") 64 | SIGRTMIN = __libc_current_sigrtmin() 65 | 66 | timer_create_ = librt.func("i", "timer_create", "ipp") 67 | timer_delete_ = librt.func("i", "timer_delete", "i") 68 | timer_settime_ = librt.func("i", "timer_settime", "PiPp") 69 | 70 | sigaction_ = libc.func("i", "sigaction", "iPp") 71 | 72 | # Create a new C struct 73 | 74 | def new(sdesc): 75 | buf = bytearray(uctypes.sizeof(sdesc)) 76 | s = uctypes.struct(uctypes.addressof(buf), sdesc, uctypes.NATIVE) 77 | return s 78 | 79 | # Posix Signal handling 80 | 81 | 82 | def sigaction(signum, handler, flags=0): 83 | sa = new(sigaction_t) 84 | sa_old = new(sigaction_t) 85 | cb = ffi.callback("v", handler, "i", lock=True) 86 | sa.sa_handler = cb.cfun() 87 | sa.sa_flags = flags 88 | r = sigaction_(signum, sa, sa_old) 89 | if r != 0: 90 | raise RuntimeError("sigaction_ error: %d (errno = %d)" % (r, os.errno())) 91 | return cb # sa_old.sa_handler 92 | 93 | # Posix Timer handling 94 | 95 | def timer_create(sig_id): 96 | sev = new(sigevent_t) 97 | # print(sev) 98 | sev.sigev_notify = SIGEV_SIGNAL 99 | sev.sigev_signo = SIGRTMIN + sig_id 100 | timerid = array.array("P", [0]) 101 | r = timer_create_(CLOCK_MONOTONIC, sev, timerid) 102 | if r != 0: 103 | raise RuntimeError("timer_create_ error: %d (errno = %d)" % (r, os.errno())) 104 | # print("timerid", hex(timerid[0])) 105 | return timerid[0] 106 | 107 | def timer_delete(tid): 108 | r = timer_delete_(tid) 109 | if r != 0: 110 | raise RuntimeError("timer_delete_ error: %d (errno = %d)" % (r, os.errno())) 111 | 112 | def timer_settime(tid, period_ms, periodic): 113 | period_ns = (period_ms * 1000000) % 1000000000 114 | period_sec = (period_ms * 1000000) // 1000000000 115 | 116 | new_val = new(itimerspec_t) 117 | new_val.it_value.tv_sec = period_sec 118 | new_val.it_value.tv_nsec = period_ns 119 | if periodic: 120 | new_val.it_interval.tv_sec = period_sec 121 | new_val.it_interval.tv_nsec = period_ns 122 | # print("new_val:", bytes(new_val)) 123 | old_val = new(itimerspec_t) 124 | # print(new_val, old_val) 125 | r = timer_settime_(tid, 0, new_val, old_val) 126 | if r != 0: 127 | raise RuntimeError("timer_settime_ error: %d (errno = %d)" % (r, os.errno())) 128 | # print("old_val:", bytes(old_val)) 129 | 130 | # Timer class 131 | 132 | class Timer: 133 | 134 | PERIODIC = 0 135 | ONE_SHOT = 1 136 | 137 | def __init__(self, id): 138 | self.id = id 139 | self._valid = False 140 | 141 | def init(self, mode=PERIODIC, period=-1, callback=None): 142 | self.tid = timer_create(self.id) 143 | self.mode = mode 144 | self.period = period 145 | self.cb = callback 146 | timer_settime(self.tid, self.period, self.mode == Timer.PERIODIC) 147 | self.handler_ref = self.handler 148 | # print("Sig %d: %s" % (SIGRTMIN + self.id, self.org_sig)) 149 | self.action = sigaction(SIGRTMIN + self.id, self.handler_ref) 150 | self._valid = True 151 | 152 | def deinit(self): 153 | if self._valid: 154 | timer_settime(self.tid, 0, self.mode == Timer.PERIODIC) 155 | # timer_delete(self.tid) 156 | self._valid = False 157 | 158 | def handler(self, signum=-1): 159 | # print('Signal handler called with signal', signum) 160 | try: 161 | self.cb(self) 162 | except: 163 | self.deinit() 164 | raise 165 | 166 | -------------------------------------------------------------------------------- /driver/rp2/rp2_dma-test.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append('.') 3 | from rp2_dma import * 4 | 5 | dma = DMA(0) 6 | src_buf = b"Hello World!"*1000 7 | dst_buf = bytearray( 12*1000 ) 8 | 9 | dma.config( 10 | src_addr = uctypes.addressof( src_buf ), 11 | dst_addr = uctypes.addressof( dst_buf ), 12 | count = len( src_buf ), 13 | src_inc = True, 14 | dst_inc = True, 15 | trig_dreq = DMA.DREQ_PERMANENT 16 | ) 17 | 18 | t0 = time.ticks_us() 19 | dma.enable() 20 | while( dma.is_busy() ): 21 | pass 22 | dma.disable() 23 | t1 = time.ticks_us() 24 | 25 | print( "dst", dst_buf[0:12], "..." ) 26 | 27 | print( "Transfer speed [B/s]:", len( src_buf )/((t1-t0)*1e-6) ) 28 | print( "@CPU freq:", machine.freq() ) 29 | -------------------------------------------------------------------------------- /driver/rp2/rp2_dma.py: -------------------------------------------------------------------------------- 1 | import time 2 | import uctypes 3 | import struct 4 | import machine 5 | import sys 6 | 7 | if sys.platform!='rp2': raise ImportError('This module can only be meaningfully used on the rp2 platform.') 8 | 9 | class DMA: 10 | DMA_BASE = const(0x50000000) 11 | 12 | DMA_EN = const(0x01 << 0) 13 | HIGH_PRIO = const(0x01 << 1) 14 | INCR_READ = const(0x01 << 4) 15 | INCR_WRITE= const(0x01 << 5) 16 | DREQ_PIO0_RX0 = const(0x04 << 15) 17 | DREQ_SPI1_TX = const(0x12 << 15) 18 | DREQ_PERMANENT= const(0x3F << 15) 19 | IRQ_QUIET = const(0x01 << 21) 20 | BUSY = const(0x01 << 24) 21 | 22 | def __init__( self, channelNumber ): 23 | offset = channelNumber * 0x40 24 | self.CHx_READ_ADDR = DMA.DMA_BASE + 0x00 + offset 25 | self.CHx_WRITE_ADDR = DMA.DMA_BASE + 0x04 + offset 26 | self.CHx_TRANS_COUNT = DMA.DMA_BASE + 0x08 + offset 27 | self.CHx_CTRL_TRIG = DMA.DMA_BASE + 0x0C + offset 28 | 29 | def config( self, src_addr, dst_addr, count, src_inc, dst_inc, trig_dreq ): 30 | machine.mem32[ self.CHx_CTRL_TRIG ] = 0 31 | machine.mem32[ self.CHx_READ_ADDR ] = src_addr 32 | machine.mem32[ self.CHx_WRITE_ADDR ] = dst_addr 33 | machine.mem32[ self.CHx_TRANS_COUNT ] = count 34 | trig_val = 0 35 | if( src_inc ): 36 | trig_val |= DMA.INCR_READ 37 | if( dst_inc ): 38 | trig_val |= DMA.INCR_WRITE 39 | trig_val |= trig_dreq 40 | machine.mem32[ self.CHx_CTRL_TRIG ] = trig_val 41 | 42 | def enable( self ): 43 | machine.mem32[ self.CHx_CTRL_TRIG ] |= DMA.DMA_EN 44 | 45 | def disable( self ): 46 | machine.mem32[ self.CHx_CTRL_TRIG ] = 0 47 | 48 | def is_busy( self ): 49 | if( machine.mem32[ self.CHx_CTRL_TRIG ] & DMA.BUSY ): 50 | return True 51 | else: 52 | return False 53 | -------------------------------------------------------------------------------- /driver/stm32/STM32F7DISC/modrk043fn48h.c: -------------------------------------------------------------------------------- 1 | #include "py/runtime.h" 2 | #include "py/mphal.h" 3 | #include "shared/runtime/softtimer.h" 4 | #include 5 | #include 6 | #include "../../../lvgl/lvgl.h" 7 | #include "../../../lv_conf.h" 8 | #include "../../include/common.h" 9 | #include "stm32746g_discovery_ts.h" 10 | #include "rk043fn48h.h" 11 | #include "ports/stm32/i2c.h" 12 | 13 | 14 | #define LCD_DISP_PIN GPIO_PIN_12 15 | #define LCD_DISP_GPIO_PORT GPIOI 16 | #define LCD_DISP_GPIO_CLK_ENABLE() __HAL_RCC_GPIOI_CLK_ENABLE() 17 | #define LCD_DISP_GPIO_CLK_DISABLE() __HAL_RCC_GPIOI_CLK_DISABLE() 18 | #define LCD_BL_CTRL_PIN GPIO_PIN_3 19 | #define LCD_BL_CTRL_GPIO_PORT GPIOK 20 | #define LCD_BL_CTRL_GPIO_CLK_ENABLE() __HAL_RCC_GPIOK_CLK_ENABLE() 21 | #define LCD_BL_CTRL_GPIO_CLK_DISABLE() __HAL_RCC_GPIOK_CLK_DISABLE() 22 | 23 | MP_REGISTER_ROOT_POINTER(void* rk043fn48h_fb[2]); 24 | 25 | LTDC_HandleTypeDef *hltdc = NULL; // handle to LTDC, referenced in stm32_it.c 26 | DMA2D_HandleTypeDef *hdma2d = NULL; // handle to DMA2D, referenced in stm32_it.c 27 | i2c_t *i2c_ts = NULL; // I2C handle for touchscreen 28 | lv_disp_t *dma2d_disp_drv = NULL; // handle to display driver 29 | lv_color_t *fb[2] = {NULL, NULL}; // framebuffer pointers 30 | uint32_t w = 0; // display width 31 | uint32_t h = 0; // display height 32 | volatile bool dma2d_pend = false; // flag of DMA2D pending operation 33 | 34 | static bool config_ltdc(void); 35 | static bool config_dma2d(void); 36 | 37 | STATIC mp_obj_t mp_rk043fn48h_framebuffer(mp_obj_t n_obj) { 38 | int n = mp_obj_get_int(n_obj) -1; 39 | 40 | if (n<0 || n>1){ 41 | return mp_const_none; 42 | } 43 | 44 | if(fb[n]==NULL){ 45 | // allocation on extRAM with 1KB alignment to speed up LTDC burst access on AHB 46 | fb[n] = MP_STATE_PORT(rk043fn48h_fb[n]) = m_malloc(sizeof(lv_color_t) * w * h + 1024); 47 | fb[n] = (lv_color_t*)((uint32_t)fb[n] + 1024 - (uint32_t)fb[n] % 1024); 48 | } 49 | return mp_obj_new_bytearray_by_ref(sizeof(lv_color_t) * w * h , (void *)fb[n]); 50 | } 51 | 52 | STATIC mp_obj_t mp_rk043fn48h_init(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { 53 | enum { ARG_w, ARG_h }; 54 | static const mp_arg_t allowed_args[] = { 55 | { MP_QSTR_w, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 480} }, 56 | { MP_QSTR_h, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 272} }, 57 | }; 58 | 59 | // parse args 60 | mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; 61 | mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); 62 | 63 | w = args[ARG_w].u_int; 64 | h = args[ARG_h].u_int; 65 | 66 | mp_rk043fn48h_framebuffer(mp_obj_new_int(1)); 67 | 68 | if (fb[0] == NULL) { 69 | mp_obj_new_exception_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Failed allocating frame buffer")); 70 | } 71 | 72 | if (!config_ltdc()) { 73 | mp_obj_new_exception_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("LTDC init error")); 74 | } 75 | 76 | if (!config_dma2d()) { 77 | mp_obj_new_exception_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("DMA2D init error")); 78 | } 79 | 80 | i2c_ts = I2C3; 81 | i2c_init(i2c_ts, MICROPY_HW_I2C3_SCL, MICROPY_HW_I2C3_SDA, 400000, 100); 82 | 83 | if (BSP_TS_Init(w, h) != TS_OK) { 84 | mp_obj_new_exception_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Touchscreen init error")); 85 | } 86 | 87 | return mp_const_none; 88 | } 89 | 90 | STATIC mp_obj_t mp_rk043fn48h_deinit() { 91 | if (hdma2d) { 92 | HAL_DMA2D_DeInit(hdma2d); 93 | hdma2d = NULL; 94 | } 95 | 96 | if (hltdc) { 97 | HAL_LTDC_DeInit(hltdc); 98 | HAL_GPIO_WritePin(LCD_DISP_GPIO_PORT, LCD_DISP_PIN, GPIO_PIN_RESET); 99 | HAL_GPIO_WritePin(LCD_BL_CTRL_GPIO_PORT, LCD_BL_CTRL_PIN, GPIO_PIN_RESET); 100 | hltdc = NULL; 101 | } 102 | 103 | if(fb[0]!=NULL){ 104 | m_free(MP_STATE_PORT(rk043fn48h_fb[0])); 105 | fb[0]=NULL; 106 | } 107 | 108 | if(fb[1]!=NULL){ 109 | m_free(MP_STATE_PORT(rk043fn48h_fb[1])); 110 | fb[1]=NULL; 111 | } 112 | 113 | BSP_TS_DeInit(); 114 | 115 | return mp_const_none; 116 | } 117 | 118 | STATIC void mp_rk043fn48h_flush(lv_disp_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) { 119 | if ((lv_area_get_width(area) == w) && (lv_area_get_height(area) == h)) { 120 | dma2d_disp_drv = disp_drv; 121 | SCB_CleanInvalidateDCache(); 122 | HAL_LTDC_SetAddress_NoReload(hltdc, (uint32_t)color_p, 1); 123 | HAL_LTDC_Reload(hltdc, LTDC_RELOAD_VERTICAL_BLANKING); 124 | } else { 125 | hdma2d->Init.Mode = DMA2D_M2M; 126 | hdma2d->Init.OutputOffset = w - lv_area_get_width(area); 127 | dma2d_disp_drv = disp_drv; 128 | dma2d_pend = true; 129 | SCB_CleanInvalidateDCache(); 130 | HAL_DMA2D_Init(hdma2d); 131 | HAL_DMA2D_Start_IT(hdma2d, 132 | (uint32_t)color_p, 133 | (uint32_t)(fb[0] + area->x1 + area->y1 * w), 134 | lv_area_get_width(area), 135 | lv_area_get_height(area)); 136 | } 137 | } 138 | 139 | void DMA2D_TransferComplete(DMA2D_HandleTypeDef *hdma2d) { 140 | lv_disp_flush_ready(dma2d_disp_drv); 141 | dma2d_pend = false; 142 | } 143 | 144 | void HAL_LTDC_ReloadEventCallback(LTDC_HandleTypeDef *hltdc) { 145 | lv_disp_flush_ready(dma2d_disp_drv); 146 | } 147 | 148 | STATIC void mp_rk043fn48h_ts_read(struct _lv_indev_t *indev_drv, lv_indev_data_t *data) { 149 | static TS_StateTypeDef ts_state = {0}; 150 | static lv_coord_t lastX = 0; 151 | static lv_coord_t lastY = 0; 152 | 153 | BSP_TS_GetState(&ts_state); 154 | if (ts_state.touchDetected) { 155 | data->point.x = lastX = ts_state.touchX[0]; 156 | data->point.y = lastY = ts_state.touchY[0]; 157 | data->state = LV_INDEV_STATE_PR; 158 | } else { 159 | data->point.x = lastX; 160 | data->point.y = lastY; 161 | data->state = LV_INDEV_STATE_REL; 162 | } 163 | } 164 | 165 | STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mp_rk043fn48h_init_obj, 0, mp_rk043fn48h_init); 166 | STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_rk043fn48h_deinit_obj, mp_rk043fn48h_deinit); 167 | STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_rk043fn48h_framebuffer_obj, mp_rk043fn48h_framebuffer); 168 | DEFINE_PTR_OBJ(mp_rk043fn48h_flush); 169 | DEFINE_PTR_OBJ(mp_rk043fn48h_ts_read); 170 | 171 | STATIC const mp_rom_map_elem_t rk043fn48h_globals_table[] = { 172 | { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_rk043fn48h) }, 173 | { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&mp_rk043fn48h_init_obj) }, 174 | { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&mp_rk043fn48h_deinit_obj) }, 175 | { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&PTR_OBJ(mp_rk043fn48h_flush))}, 176 | { MP_ROM_QSTR(MP_QSTR_ts_read), MP_ROM_PTR(&PTR_OBJ(mp_rk043fn48h_ts_read))}, 177 | { MP_ROM_QSTR(MP_QSTR_framebuffer), MP_ROM_PTR(&PTR_OBJ(mp_rk043fn48h_framebuffer))}, 178 | }; 179 | 180 | STATIC MP_DEFINE_CONST_DICT( 181 | mp_module_rk043fn48h_globals, 182 | rk043fn48h_globals_table 183 | ); 184 | 185 | const mp_obj_module_t mp_module_rk043fn48h = { 186 | .base = { &mp_type_module }, 187 | .globals = (mp_obj_dict_t *)&mp_module_rk043fn48h_globals 188 | }; 189 | 190 | MP_REGISTER_MODULE(MP_QSTR_rk043fn48h, mp_module_rk043fn48h); 191 | 192 | static bool config_ltdc(void) { 193 | RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; 194 | static LTDC_HandleTypeDef hltdc_F = {0}; 195 | LTDC_LayerCfgTypeDef pLayerCfg = {0}; 196 | 197 | PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC; 198 | PeriphClkInitStruct.PLLSAI.PLLSAIN = 192; 199 | PeriphClkInitStruct.PLLSAI.PLLSAIR = RK043FN48H_FREQUENCY_DIVIDER; 200 | PeriphClkInitStruct.PLLSAIDivR = RCC_PLLSAIDIVR_4; 201 | if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) { 202 | return false; 203 | } 204 | 205 | hltdc_F.Init.HSPolarity = LTDC_HSPOLARITY_AL; 206 | hltdc_F.Init.VSPolarity = LTDC_VSPOLARITY_AL; 207 | hltdc_F.Init.DEPolarity = LTDC_DEPOLARITY_AL; 208 | hltdc_F.Init.PCPolarity = LTDC_PCPOLARITY_IPC; 209 | hltdc_F.Init.HorizontalSync = (RK043FN48H_HSYNC - 1); 210 | hltdc_F.Init.VerticalSync = (RK043FN48H_VSYNC - 1); 211 | hltdc_F.Init.AccumulatedHBP = (RK043FN48H_HSYNC + RK043FN48H_HBP - 1); 212 | hltdc_F.Init.AccumulatedVBP = (RK043FN48H_VSYNC + RK043FN48H_VBP - 1); 213 | hltdc_F.Init.AccumulatedActiveH = (RK043FN48H_HEIGHT + RK043FN48H_VSYNC + RK043FN48H_VBP - 1); 214 | hltdc_F.Init.AccumulatedActiveW = (RK043FN48H_WIDTH + RK043FN48H_HSYNC + RK043FN48H_HBP - 1); 215 | hltdc_F.Init.TotalHeigh = (RK043FN48H_HEIGHT + RK043FN48H_VSYNC + RK043FN48H_VBP + RK043FN48H_VFP - 1); 216 | hltdc_F.Init.TotalWidth = (RK043FN48H_WIDTH + RK043FN48H_HSYNC + RK043FN48H_HBP + RK043FN48H_HFP - 1); 217 | hltdc_F.Init.Backcolor.Blue = 0; 218 | hltdc_F.Init.Backcolor.Green = 0; 219 | hltdc_F.Init.Backcolor.Red = 0; 220 | hltdc_F.Instance = LTDC; 221 | 222 | pLayerCfg.WindowX0 = 0; 223 | pLayerCfg.WindowX1 = w; 224 | pLayerCfg.WindowY0 = 0; 225 | pLayerCfg.WindowY1 = h; 226 | 227 | #if (LV_COLOR_DEPTH == 32) 228 | pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_ARGB8888; 229 | #elif (LV_COLOR_DEPTH == 16) 230 | pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB565; 231 | #else 232 | #error "modrk043fn48h: LV_COLOR_DEPTH not supported" 233 | #endif 234 | 235 | pLayerCfg.FBStartAdress = (uint32_t)fb[0]; 236 | pLayerCfg.Alpha = 255; 237 | pLayerCfg.Alpha0 = 0; 238 | pLayerCfg.Backcolor.Blue = 0; 239 | pLayerCfg.Backcolor.Green = 0; 240 | pLayerCfg.Backcolor.Red = 0; 241 | pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA; 242 | pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA; 243 | pLayerCfg.ImageWidth = w; 244 | pLayerCfg.ImageHeight = h; 245 | 246 | hltdc = &hltdc_F; 247 | 248 | if (HAL_LTDC_Init(hltdc) != HAL_OK) { 249 | return false; 250 | } 251 | 252 | if (HAL_LTDC_ConfigLayer(hltdc, &pLayerCfg, 1) != HAL_OK) { 253 | return false; 254 | } 255 | 256 | return true; 257 | } 258 | 259 | static bool config_dma2d(void) { 260 | static DMA2D_HandleTypeDef Dma2dHandle = {0}; 261 | 262 | #if (LV_COLOR_DEPTH == 32) 263 | uint32_t OutColorMode = DMA2D_OUTPUT_ARGB8888; 264 | uint32_t InColorMode = DMA2D_INPUT_ARGB8888; 265 | #elif (LV_COLOR_DEPTH == 16) 266 | uint32_t OutColorMode = DMA2D_OUTPUT_RGB565; 267 | uint32_t InColorMode = DMA2D_INPUT_RGB565; 268 | #else 269 | #error "modrk043fn48h: LV_COLOR_DEPTH not supported" 270 | #endif 271 | 272 | Dma2dHandle.Init.Mode = DMA2D_M2M_BLEND; 273 | Dma2dHandle.Init.ColorMode = OutColorMode; 274 | Dma2dHandle.Init.OutputOffset = 0x0; 275 | Dma2dHandle.LayerCfg[1].AlphaMode = DMA2D_REPLACE_ALPHA; 276 | Dma2dHandle.LayerCfg[1].InputAlpha = 0xFF; 277 | Dma2dHandle.LayerCfg[1].InputColorMode = InColorMode; 278 | Dma2dHandle.LayerCfg[1].InputOffset = 0x0; 279 | Dma2dHandle.LayerCfg[0].AlphaMode = DMA2D_REPLACE_ALPHA; 280 | Dma2dHandle.LayerCfg[0].InputAlpha = 0xFF; 281 | Dma2dHandle.LayerCfg[0].InputColorMode = InColorMode; 282 | Dma2dHandle.LayerCfg[0].InputOffset = 0x0; 283 | Dma2dHandle.Instance = DMA2D; 284 | Dma2dHandle.XferCpltCallback = DMA2D_TransferComplete; 285 | 286 | if (HAL_DMA2D_Init(&Dma2dHandle) != HAL_OK) { 287 | return false; 288 | } 289 | 290 | hdma2d = &Dma2dHandle; 291 | 292 | HAL_DMA2D_ConfigLayer(&Dma2dHandle, 0); 293 | HAL_DMA2D_ConfigLayer(&Dma2dHandle, 1); 294 | 295 | return true; 296 | } 297 | 298 | void HAL_LTDC_MspInit(LTDC_HandleTypeDef *hltdc) { 299 | GPIO_InitTypeDef GPIO_Init_Structure; 300 | 301 | __HAL_RCC_LTDC_CLK_ENABLE(); 302 | __HAL_RCC_GPIOE_CLK_ENABLE(); 303 | __HAL_RCC_GPIOG_CLK_ENABLE(); 304 | __HAL_RCC_GPIOI_CLK_ENABLE(); 305 | __HAL_RCC_GPIOJ_CLK_ENABLE(); 306 | __HAL_RCC_GPIOK_CLK_ENABLE(); 307 | 308 | GPIO_Init_Structure.Pin = GPIO_PIN_4; 309 | GPIO_Init_Structure.Mode = GPIO_MODE_AF_PP; 310 | GPIO_Init_Structure.Pull = GPIO_NOPULL; 311 | GPIO_Init_Structure.Speed = GPIO_SPEED_FAST; 312 | GPIO_Init_Structure.Alternate = GPIO_AF14_LTDC; 313 | HAL_GPIO_Init(GPIOE, &GPIO_Init_Structure); 314 | 315 | GPIO_Init_Structure.Pin = GPIO_PIN_12; 316 | GPIO_Init_Structure.Mode = GPIO_MODE_AF_PP; 317 | GPIO_Init_Structure.Alternate = GPIO_AF9_LTDC; 318 | HAL_GPIO_Init(GPIOG, &GPIO_Init_Structure); 319 | 320 | GPIO_Init_Structure.Pin = GPIO_PIN_9 | GPIO_PIN_10 | \ 321 | GPIO_PIN_14 | GPIO_PIN_15; 322 | GPIO_Init_Structure.Mode = GPIO_MODE_AF_PP; 323 | GPIO_Init_Structure.Alternate = GPIO_AF14_LTDC; 324 | HAL_GPIO_Init(GPIOI, &GPIO_Init_Structure); 325 | 326 | GPIO_Init_Structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | \ 327 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 | \ 328 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | \ 329 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15; 330 | GPIO_Init_Structure.Mode = GPIO_MODE_AF_PP; 331 | GPIO_Init_Structure.Alternate = GPIO_AF14_LTDC; 332 | HAL_GPIO_Init(GPIOJ, &GPIO_Init_Structure); 333 | 334 | GPIO_Init_Structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_4 | \ 335 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7; 336 | GPIO_Init_Structure.Mode = GPIO_MODE_AF_PP; 337 | GPIO_Init_Structure.Alternate = GPIO_AF14_LTDC; 338 | HAL_GPIO_Init(GPIOK, &GPIO_Init_Structure); 339 | 340 | GPIO_Init_Structure.Pin = GPIO_PIN_12; 341 | GPIO_Init_Structure.Mode = GPIO_MODE_OUTPUT_PP; 342 | HAL_GPIO_Init(GPIOI, &GPIO_Init_Structure); 343 | 344 | GPIO_Init_Structure.Pin = GPIO_PIN_3; 345 | GPIO_Init_Structure.Mode = GPIO_MODE_OUTPUT_PP; 346 | HAL_GPIO_Init(GPIOK, &GPIO_Init_Structure); 347 | 348 | HAL_GPIO_WritePin(LCD_DISP_GPIO_PORT, LCD_DISP_PIN, GPIO_PIN_SET); 349 | HAL_GPIO_WritePin(LCD_BL_CTRL_GPIO_PORT, LCD_BL_CTRL_PIN, GPIO_PIN_SET); 350 | 351 | HAL_NVIC_SetPriority(LTDC_IRQn, 1, 0); 352 | HAL_NVIC_EnableIRQ(LTDC_IRQn); 353 | } 354 | 355 | 356 | void HAL_LTDC_MspDeInit(LTDC_HandleTypeDef *hltdc) { 357 | __HAL_RCC_LTDC_FORCE_RESET(); 358 | __HAL_RCC_LTDC_RELEASE_RESET(); 359 | } 360 | 361 | 362 | void HAL_DMA2D_MspInit(DMA2D_HandleTypeDef *hdma2d) { 363 | __HAL_RCC_DMA2D_CLK_ENABLE(); 364 | HAL_NVIC_SetPriority(DMA2D_IRQn, 1, 1); 365 | HAL_NVIC_EnableIRQ(DMA2D_IRQn); 366 | } 367 | 368 | void TS_IO_Init(void) { 369 | } 370 | 371 | void TS_IO_Write(uint8_t Addr, uint8_t Reg, uint8_t Value) { 372 | static uint8_t buff[2]; 373 | buff[0] = Reg; 374 | buff[1] = Value; 375 | uint16_t addr = Addr >> 1; 376 | i2c_writeto(i2c_ts, addr, buff, 2, true); 377 | } 378 | 379 | uint8_t TS_IO_Read(uint8_t Addr, uint8_t Reg) { 380 | uint8_t Value = 0xff; 381 | uint16_t addr = Addr >> 1; 382 | i2c_writeto(i2c_ts, addr, &Reg, 1, true); 383 | i2c_readfrom(i2c_ts, addr, &Value, 1, true); 384 | return Value; 385 | } 386 | 387 | void TS_IO_Delay(uint32_t Delay) { 388 | HAL_Delay(Delay); 389 | } 390 | 391 | -------------------------------------------------------------------------------- /driver/stm32/STM32F7DISC/rk043fn48h.h: -------------------------------------------------------------------------------- 1 | /** 2 | ****************************************************************************** 3 | * @file rk043fn48h.h 4 | * @author MCD Application Team 5 | * @brief This file contains all the constants parameters for the RK043FN48H-CT672B 6 | * LCD component. 7 | ****************************************************************************** 8 | * @attention 9 | * 10 | *

© COPYRIGHT(c) 2015 STMicroelectronics

11 | * 12 | * Redistribution and use in source and binary forms, with or without modification, 13 | * are permitted provided that the following conditions are met: 14 | * 1. Redistributions of source code must retain the above copyright notice, 15 | * this list of conditions and the following disclaimer. 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 3. Neither the name of STMicroelectronics nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software 21 | * without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 24 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 27 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 29 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * 34 | ****************************************************************************** 35 | */ 36 | 37 | /* Define to prevent recursive inclusion -------------------------------------*/ 38 | #ifndef __RK043FN48H_H 39 | #define __RK043FN48H_H 40 | 41 | #ifdef __cplusplus 42 | extern "C" { 43 | #endif 44 | 45 | /* Includes ------------------------------------------------------------------*/ 46 | 47 | /** @addtogroup BSP 48 | * @{ 49 | */ 50 | 51 | /** @addtogroup Components 52 | * @{ 53 | */ 54 | 55 | /** @addtogroup rk043fn48h 56 | * @{ 57 | */ 58 | 59 | /** @defgroup RK043FN48H_Exported_Types 60 | * @{ 61 | */ 62 | 63 | /** 64 | * @} 65 | */ 66 | 67 | /** @defgroup RK043FN48H_Exported_Constants 68 | * @{ 69 | */ 70 | 71 | /** 72 | * @brief RK043FN48H Size 73 | */ 74 | #define RK043FN48H_WIDTH ((uint16_t)480) /* LCD PIXEL WIDTH */ 75 | #define RK043FN48H_HEIGHT ((uint16_t)272) /* LCD PIXEL HEIGHT */ 76 | 77 | /** 78 | * @brief RK043FN48H Timing 79 | */ 80 | #define RK043FN48H_HSYNC ((uint16_t)41) /* Horizontal synchronization */ 81 | #define RK043FN48H_HBP ((uint16_t)13) /* Horizontal back porch */ 82 | #define RK043FN48H_HFP ((uint16_t)32) /* Horizontal front porch */ 83 | #define RK043FN48H_VSYNC ((uint16_t)10) /* Vertical synchronization */ 84 | #define RK043FN48H_VBP ((uint16_t)2) /* Vertical back porch */ 85 | #define RK043FN48H_VFP ((uint16_t)2) /* Vertical front porch */ 86 | 87 | /** 88 | * @brief RK043FN48H frequency divider 89 | */ 90 | #define RK043FN48H_FREQUENCY_DIVIDER 5 /* LCD Frequency divider */ 91 | /** 92 | * @} 93 | */ 94 | 95 | /** @defgroup RK043FN48H_Exported_Functions 96 | * @{ 97 | */ 98 | 99 | /** 100 | * @} 101 | */ 102 | #ifdef __cplusplus 103 | } 104 | #endif 105 | 106 | #endif /* __RK043FN48H_H */ 107 | /** 108 | * @} 109 | */ 110 | 111 | /** 112 | * @} 113 | */ 114 | 115 | /** 116 | * @} 117 | */ 118 | 119 | /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 120 | -------------------------------------------------------------------------------- /driver/stm32/STM32F7DISC/stm32746g_discovery.h: -------------------------------------------------------------------------------- 1 | /** 2 | ****************************************************************************** 3 | * @file stm32746g_discovery.h 4 | * @author MCD Application Team 5 | * @brief This file contains definitions for STM32746G_DISCOVERY's LEDs, 6 | * push-buttons and COM ports hardware resources. 7 | ****************************************************************************** 8 | * @attention 9 | * 10 | *

© COPYRIGHT(c) 2016 STMicroelectronics

11 | * 12 | * Redistribution and use in source and binary forms, with or without modification, 13 | * are permitted provided that the following conditions are met: 14 | * 1. Redistributions of source code must retain the above copyright notice, 15 | * this list of conditions and the following disclaimer. 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 3. Neither the name of STMicroelectronics nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software 21 | * without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 24 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 27 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 29 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * 34 | ****************************************************************************** 35 | */ 36 | 37 | /* Define to prevent recursive inclusion -------------------------------------*/ 38 | #ifndef __STM32746G_DISCOVERY_H 39 | #define __STM32746G_DISCOVERY_H 40 | 41 | #ifdef __cplusplus 42 | extern "C" { 43 | #endif 44 | 45 | /* Includes ------------------------------------------------------------------*/ 46 | #include "stm32f7xx_hal.h" 47 | 48 | /** @addtogroup BSP 49 | * @{ 50 | */ 51 | 52 | /** @addtogroup STM32746G_DISCOVERY 53 | * @{ 54 | */ 55 | 56 | /** @addtogroup STM32746G_DISCOVERY_LOW_LEVEL 57 | * @{ 58 | */ 59 | 60 | /** @defgroup STM32746G_DISCOVERY_LOW_LEVEL_Exported_Types STM32746G_DISCOVERY_LOW_LEVEL Exported Types 61 | * @{ 62 | */ 63 | typedef enum 64 | { 65 | LED1 = 0, 66 | LED_GREEN = LED1, 67 | }Led_TypeDef; 68 | 69 | typedef enum 70 | { 71 | BUTTON_WAKEUP = 0, 72 | BUTTON_TAMPER = 1, 73 | BUTTON_KEY = 2 74 | }Button_TypeDef; 75 | 76 | typedef enum 77 | { 78 | BUTTON_MODE_GPIO = 0, 79 | BUTTON_MODE_EXTI = 1 80 | }ButtonMode_TypeDef; 81 | 82 | typedef enum 83 | { 84 | COM1 = 0, 85 | COM2 = 1 86 | }COM_TypeDef; 87 | /** 88 | * @} 89 | */ 90 | 91 | /** @defgroup STM32746G_DISCOVERY_LOW_LEVEL_Exported_Constants STM32746G_DISCOVERY_LOW_LEVEL Exported Constants 92 | * @{ 93 | */ 94 | 95 | /** 96 | * @brief Define for STM32746G_DISCOVERY board 97 | */ 98 | #if !defined (USE_STM32746G_DISCO) 99 | #define USE_STM32746G_DISCO 100 | #endif 101 | 102 | /** @addtogroup STM32746G_DISCOVERY_LOW_LEVEL_LED 103 | * @{ 104 | */ 105 | 106 | #define LEDn ((uint8_t)1) 107 | 108 | #define LED1_GPIO_PORT GPIOI 109 | #define LED1_GPIO_CLK_ENABLE() __HAL_RCC_GPIOI_CLK_ENABLE() 110 | #define LED1_GPIO_CLK_DISABLE() __HAL_RCC_GPIOI_CLK_DISABLE() 111 | #define LED1_PIN GPIO_PIN_1 112 | 113 | /** 114 | * @} 115 | */ 116 | 117 | /** @addtogroup STM32746G_DISCOVERY_LOW_LEVEL_BUTTON 118 | * @{ 119 | */ 120 | #define BUTTONn ((uint8_t)3) 121 | 122 | /** 123 | * @brief Wakeup push-button 124 | */ 125 | #define WAKEUP_BUTTON_PIN GPIO_PIN_11 126 | #define WAKEUP_BUTTON_GPIO_PORT GPIOI 127 | #define WAKEUP_BUTTON_GPIO_CLK_ENABLE() __HAL_RCC_GPIOI_CLK_ENABLE() 128 | #define WAKEUP_BUTTON_GPIO_CLK_DISABLE() __HAL_RCC_GPIOI_CLK_DISABLE() 129 | #define WAKEUP_BUTTON_EXTI_IRQn EXTI15_10_IRQn 130 | 131 | /** 132 | * @brief Tamper push-button 133 | */ 134 | #define TAMPER_BUTTON_PIN GPIO_PIN_11 135 | #define TAMPER_BUTTON_GPIO_PORT GPIOI 136 | #define TAMPER_BUTTON_GPIO_CLK_ENABLE() __HAL_RCC_GPIOI_CLK_ENABLE() 137 | #define TAMPER_BUTTON_GPIO_CLK_DISABLE() __HAL_RCC_GPIOI_CLK_DISABLE() 138 | #define TAMPER_BUTTON_EXTI_IRQn EXTI15_10_IRQn 139 | 140 | /** 141 | * @brief Key push-button 142 | */ 143 | #define KEY_BUTTON_PIN GPIO_PIN_11 144 | #define KEY_BUTTON_GPIO_PORT GPIOI 145 | #define KEY_BUTTON_GPIO_CLK_ENABLE() __HAL_RCC_GPIOI_CLK_ENABLE() 146 | #define KEY_BUTTON_GPIO_CLK_DISABLE() __HAL_RCC_GPIOI_CLK_DISABLE() 147 | #define KEY_BUTTON_EXTI_IRQn EXTI15_10_IRQn 148 | 149 | #define BUTTONx_GPIO_CLK_ENABLE(__INDEX__) do { if((__INDEX__) == 0) WAKEUP_BUTTON_GPIO_CLK_ENABLE(); else\ 150 | if((__INDEX__) == 1) TAMPER_BUTTON_GPIO_CLK_ENABLE(); else\ 151 | KEY_BUTTON_GPIO_CLK_ENABLE(); } while(0) 152 | 153 | #define BUTTONx_GPIO_CLK_DISABLE(__INDEX__) (((__INDEX__) == 0) ? WAKEUP_BUTTON_GPIO_CLK_DISABLE() :\ 154 | ((__INDEX__) == 1) ? TAMPER_BUTTON_GPIO_CLK_DISABLE() : KEY_BUTTON_GPIO_CLK_DISABLE()) 155 | 156 | /** 157 | * @} 158 | */ 159 | 160 | /** @addtogroup STM32746G_DISCOVERY_LOW_LEVEL_SIGNAL 161 | * @{ 162 | */ 163 | #define SIGNALn ((uint8_t)1) 164 | 165 | /** 166 | * @brief SD-detect signal 167 | */ 168 | #define SD_DETECT_PIN GPIO_PIN_13 169 | #define SD_DETECT_GPIO_PORT GPIOC 170 | #define SD_DETECT_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE() 171 | #define SD_DETECT_GPIO_CLK_DISABLE() __HAL_RCC_GPIOC_CLK_DISABLE() 172 | #define SD_DETECT_EXTI_IRQn EXTI15_10_IRQn 173 | 174 | /** 175 | * @brief Touch screen interrupt signal 176 | */ 177 | #define TS_INT_PIN GPIO_PIN_13 178 | #define TS_INT_GPIO_PORT GPIOI 179 | #define TS_INT_GPIO_CLK_ENABLE() __HAL_RCC_GPIOI_CLK_ENABLE() 180 | #define TS_INT_GPIO_CLK_DISABLE() __HAL_RCC_GPIOI_CLK_DISABLE() 181 | #define TS_INT_EXTI_IRQn EXTI15_10_IRQn 182 | 183 | /** 184 | * @} 185 | */ 186 | 187 | /** @addtogroup STM32746G_DISCOVERY_LOW_LEVEL_COM 188 | * @{ 189 | */ 190 | #define COMn ((uint8_t)1) 191 | 192 | /** 193 | * @brief Definition for COM port1, connected to USART1 194 | */ 195 | #define DISCOVERY_COM1 USART1 196 | #define DISCOVERY_COM1_CLK_ENABLE() __HAL_RCC_USART1_CLK_ENABLE() 197 | #define DISCOVERY_COM1_CLK_DISABLE() __HAL_RCC_USART1_CLK_DISABLE() 198 | 199 | #define DISCOVERY_COM1_TX_PIN GPIO_PIN_9 200 | #define DISCOVERY_COM1_TX_GPIO_PORT GPIOA 201 | #define DISCOVERY_COM1_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() 202 | #define DISCOVERY_COM1_TX_GPIO_CLK_DISABLE() __HAL_RCC_GPIOA_CLK_DISABLE() 203 | #define DISCOVERY_COM1_TX_AF GPIO_AF7_USART1 204 | 205 | #define DISCOVERY_COM1_RX_PIN GPIO_PIN_7 206 | #define DISCOVERY_COM1_RX_GPIO_PORT GPIOB 207 | #define DISCOVERY_COM1_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() 208 | #define DISCOVERY_COM1_RX_GPIO_CLK_DISABLE() __HAL_RCC_GPIOB_CLK_DISABLE() 209 | #define DISCOVERY_COM1_RX_AF GPIO_AF7_USART1 210 | 211 | #define DISCOVERY_COM1_IRQn USART1_IRQn 212 | 213 | #define DISCOVERY_COMx_CLK_ENABLE(__INDEX__) do { if((__INDEX__) == COM1) DISCOVERY_COM1_CLK_ENABLE(); } while(0) 214 | #define DISCOVERY_COMx_CLK_DISABLE(__INDEX__) (((__INDEX__) == 0) ? DISCOVERY_COM1_CLK_DISABLE() : 0) 215 | 216 | #define DISCOVERY_COMx_TX_GPIO_CLK_ENABLE(__INDEX__) do { if((__INDEX__) == COM1) DISCOVERY_COM1_TX_GPIO_CLK_ENABLE(); } while(0) 217 | #define DISCOVERY_COMx_TX_GPIO_CLK_DISABLE(__INDEX__) (((__INDEX__) == 0) ? DISCOVERY_COM1_TX_GPIO_CLK_DISABLE() : 0) 218 | 219 | #define DISCOVERY_COMx_RX_GPIO_CLK_ENABLE(__INDEX__) do { if((__INDEX__) == COM1) DISCOVERY_COM1_RX_GPIO_CLK_ENABLE(); } while(0) 220 | #define DISCOVERY_COMx_RX_GPIO_CLK_DISABLE(__INDEX__) (((__INDEX__) == 0) ? DISCOVERY_COM1_RX_GPIO_CLK_DISABLE() : 0) 221 | 222 | /* Exported constant IO ------------------------------------------------------*/ 223 | 224 | #define LCD_I2C_ADDRESS ((uint16_t)0x70) 225 | #define CAMERA_I2C_ADDRESS ((uint16_t)0x60) 226 | #define AUDIO_I2C_ADDRESS ((uint16_t)0x34) 227 | #define EEPROM_I2C_ADDRESS_A01 ((uint16_t)0xA0) 228 | #define EEPROM_I2C_ADDRESS_A02 ((uint16_t)0xA6) 229 | #define TS_I2C_ADDRESS ((uint16_t)0x70) 230 | 231 | /* I2C clock speed configuration (in Hz) 232 | WARNING: 233 | Make sure that this define is not already declared in other files (ie. 234 | stm32746g_discovery.h file). It can be used in parallel by other modules. */ 235 | #ifndef I2C_SPEED 236 | #define I2C_SPEED ((uint32_t)100000) 237 | #endif /* I2C_SPEED */ 238 | 239 | /* User can use this section to tailor I2Cx/I2Cx instance used and associated 240 | resources */ 241 | /* Definition for AUDIO and LCD I2Cx resources */ 242 | #define DISCOVERY_AUDIO_I2Cx I2C3 243 | #define DISCOVERY_AUDIO_I2Cx_CLK_ENABLE() __HAL_RCC_I2C3_CLK_ENABLE() 244 | #define DISCOVERY_AUDIO_DMAx_CLK_ENABLE() __HAL_RCC_DMA1_CLK_ENABLE() 245 | #define DISCOVERY_AUDIO_I2Cx_SCL_SDA_GPIO_CLK_ENABLE() __HAL_RCC_GPIOH_CLK_ENABLE() 246 | 247 | #define DISCOVERY_AUDIO_I2Cx_FORCE_RESET() __HAL_RCC_I2C3_FORCE_RESET() 248 | #define DISCOVERY_AUDIO_I2Cx_RELEASE_RESET() __HAL_RCC_I2C3_RELEASE_RESET() 249 | 250 | /* Definition for I2Cx Pins */ 251 | #define DISCOVERY_AUDIO_I2Cx_SCL_PIN GPIO_PIN_7 252 | #define DISCOVERY_AUDIO_I2Cx_SCL_SDA_GPIO_PORT GPIOH 253 | #define DISCOVERY_AUDIO_I2Cx_SCL_SDA_AF GPIO_AF4_I2C3 254 | #define DISCOVERY_AUDIO_I2Cx_SDA_PIN GPIO_PIN_8 255 | 256 | /* I2C interrupt requests */ 257 | #define DISCOVERY_AUDIO_I2Cx_EV_IRQn I2C3_EV_IRQn 258 | #define DISCOVERY_AUDIO_I2Cx_ER_IRQn I2C3_ER_IRQn 259 | 260 | /* Definition for external, camera and Arduino connector I2Cx resources */ 261 | #define DISCOVERY_EXT_I2Cx I2C1 262 | #define DISCOVERY_EXT_I2Cx_CLK_ENABLE() __HAL_RCC_I2C1_CLK_ENABLE() 263 | #define DISCOVERY_EXT_DMAx_CLK_ENABLE() __HAL_RCC_DMA1_CLK_ENABLE() 264 | #define DISCOVERY_EXT_I2Cx_SCL_SDA_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() 265 | 266 | #define DISCOVERY_EXT_I2Cx_FORCE_RESET() __HAL_RCC_I2C1_FORCE_RESET() 267 | #define DISCOVERY_EXT_I2Cx_RELEASE_RESET() __HAL_RCC_I2C1_RELEASE_RESET() 268 | 269 | /* Definition for I2Cx Pins */ 270 | #define DISCOVERY_EXT_I2Cx_SCL_PIN GPIO_PIN_8 271 | #define DISCOVERY_EXT_I2Cx_SCL_SDA_GPIO_PORT GPIOB 272 | #define DISCOVERY_EXT_I2Cx_SCL_SDA_AF GPIO_AF4_I2C1 273 | #define DISCOVERY_EXT_I2Cx_SDA_PIN GPIO_PIN_9 274 | 275 | /* I2C interrupt requests */ 276 | #define DISCOVERY_EXT_I2Cx_EV_IRQn I2C1_EV_IRQn 277 | #define DISCOVERY_EXT_I2Cx_ER_IRQn I2C1_ER_IRQn 278 | 279 | /* I2C TIMING Register define when I2C clock source is SYSCLK */ 280 | /* I2C TIMING is calculated from APB1 source clock = 50 MHz */ 281 | /* Due to the big MOFSET capacity for adapting the camera level the rising time is very large (>1us) */ 282 | /* 0x40912732 takes in account the big rising and aims a clock of 100khz */ 283 | #ifndef DISCOVERY_I2Cx_TIMING 284 | #define DISCOVERY_I2Cx_TIMING ((uint32_t)0x40912732) 285 | #endif /* DISCOVERY_I2Cx_TIMING */ 286 | 287 | /** 288 | * @} 289 | */ 290 | 291 | /** 292 | * @} 293 | */ 294 | 295 | /** @defgroup STM32746G_DISCOVERY_LOW_LEVEL_Exported_Macros STM32746G_DISCOVERY_LOW_LEVEL Exported Macros 296 | * @{ 297 | */ 298 | /** 299 | * @} 300 | */ 301 | 302 | /** @addtogroup STM32746G_DISCOVERY_LOW_LEVEL_Exported_Functions 303 | * @{ 304 | */ 305 | uint32_t BSP_GetVersion(void); 306 | void BSP_LED_Init(Led_TypeDef Led); 307 | void BSP_LED_DeInit(Led_TypeDef Led); 308 | void BSP_LED_On(Led_TypeDef Led); 309 | void BSP_LED_Off(Led_TypeDef Led); 310 | void BSP_LED_Toggle(Led_TypeDef Led); 311 | void BSP_PB_Init(Button_TypeDef Button, ButtonMode_TypeDef ButtonMode); 312 | void BSP_PB_DeInit(Button_TypeDef Button); 313 | uint32_t BSP_PB_GetState(Button_TypeDef Button); 314 | void BSP_COM_Init(COM_TypeDef COM, UART_HandleTypeDef *husart); 315 | void BSP_COM_DeInit(COM_TypeDef COM, UART_HandleTypeDef *huart); 316 | 317 | /** 318 | * @} 319 | */ 320 | 321 | /** 322 | * @} 323 | */ 324 | 325 | /** 326 | * @} 327 | */ 328 | 329 | /** 330 | * @} 331 | */ 332 | 333 | #ifdef __cplusplus 334 | } 335 | #endif 336 | 337 | #endif /* __STM32746G_DISCOVERY_H */ 338 | 339 | /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 340 | -------------------------------------------------------------------------------- /driver/stm32/STM32F7DISC/stm32746g_discovery_ts.h: -------------------------------------------------------------------------------- 1 | /** 2 | ****************************************************************************** 3 | * @file stm32746g_discovery_ts.h 4 | * @author MCD Application Team 5 | * @brief This file contains the common defines and functions prototypes for 6 | * the stm32746g_discovery_ts.c driver. 7 | ****************************************************************************** 8 | * @attention 9 | * 10 | *

© COPYRIGHT(c) 2016 STMicroelectronics

11 | * 12 | * Redistribution and use in source and binary forms, with or without modification, 13 | * are permitted provided that the following conditions are met: 14 | * 1. Redistributions of source code must retain the above copyright notice, 15 | * this list of conditions and the following disclaimer. 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 3. Neither the name of STMicroelectronics nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software 21 | * without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 24 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 27 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 29 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * 34 | ****************************************************************************** 35 | */ 36 | 37 | /* Define to prevent recursive inclusion -------------------------------------*/ 38 | #ifndef __STM32746G_DISCOVERY_TS_H 39 | #define __STM32746G_DISCOVERY_TS_H 40 | 41 | #ifdef __cplusplus 42 | extern "C" { 43 | #endif 44 | 45 | /* Includes ------------------------------------------------------------------*/ 46 | #include "stm32746g_discovery.h" 47 | /* Include touch screen FT5336 component Driver */ 48 | #include "ft5336.h" 49 | 50 | /** @addtogroup BSP 51 | * @{ 52 | */ 53 | 54 | /** @addtogroup STM32746G_DISCOVERY 55 | * @{ 56 | */ 57 | 58 | /** @addtogroup STM32746G_DISCOVERY_TS 59 | * @{ 60 | */ 61 | 62 | /** @defgroup STM32746G_DISCOVERY_TS_Exported_Constants STM32746G_DISCOVERY_TS Exported Constants 63 | * @{ 64 | */ 65 | 66 | /** @brief With FT5336 : maximum 5 touches detected simultaneously 67 | */ 68 | #define TS_MAX_NB_TOUCH ((uint32_t) FT5336_MAX_DETECTABLE_TOUCH) 69 | 70 | #define TS_NO_IRQ_PENDING ((uint8_t) 0) 71 | #define TS_IRQ_PENDING ((uint8_t) 1) 72 | 73 | #define TS_SWAP_NONE ((uint8_t) 0x01) 74 | #define TS_SWAP_X ((uint8_t) 0x02) 75 | #define TS_SWAP_Y ((uint8_t) 0x04) 76 | #define TS_SWAP_XY ((uint8_t) 0x08) 77 | 78 | /** 79 | * @} 80 | */ 81 | 82 | /** @defgroup STM32746G_DISCOVERY_TS_Exported_Types STM32746G_DISCOVERY_TS Exported Types 83 | * @{ 84 | */ 85 | /** 86 | * @brief TS_StateTypeDef 87 | * Define TS State structure 88 | */ 89 | typedef struct 90 | { 91 | uint8_t touchDetected; /*!< Total number of active touches detected at last scan */ 92 | uint16_t touchX[TS_MAX_NB_TOUCH]; /*!< Touch X[0], X[1] coordinates on 12 bits */ 93 | uint16_t touchY[TS_MAX_NB_TOUCH]; /*!< Touch Y[0], Y[1] coordinates on 12 bits */ 94 | 95 | #if (TS_MULTI_TOUCH_SUPPORTED == 1) 96 | uint8_t touchWeight[TS_MAX_NB_TOUCH]; /*!< Touch_Weight[0], Touch_Weight[1] : weight property of touches */ 97 | uint8_t touchEventId[TS_MAX_NB_TOUCH]; /*!< Touch_EventId[0], Touch_EventId[1] : take value of type @ref TS_TouchEventTypeDef */ 98 | uint8_t touchArea[TS_MAX_NB_TOUCH]; /*!< Touch_Area[0], Touch_Area[1] : touch area of each touch */ 99 | uint32_t gestureId; /*!< type of gesture detected : take value of type @ref TS_GestureIdTypeDef */ 100 | #endif /* TS_MULTI_TOUCH_SUPPORTED == 1 */ 101 | 102 | } TS_StateTypeDef; 103 | 104 | /** 105 | * @} 106 | */ 107 | 108 | /** @defgroup STM32746G_DISCOVERY_TS_Exported_Constants STM32746G_DISCOVERY_TS Exported Constants 109 | * @{ 110 | */ 111 | 112 | typedef enum 113 | { 114 | TS_OK = 0x00, /*!< Touch Ok */ 115 | TS_ERROR = 0x01, /*!< Touch Error */ 116 | TS_TIMEOUT = 0x02, /*!< Touch Timeout */ 117 | TS_DEVICE_NOT_FOUND = 0x03 /*!< Touchscreen device not found */ 118 | }TS_StatusTypeDef; 119 | 120 | /** 121 | * @brief TS_GestureIdTypeDef 122 | * Define Possible managed gesture identification values returned by touch screen 123 | * driver. 124 | */ 125 | typedef enum 126 | { 127 | GEST_ID_NO_GESTURE = 0x00, /*!< Gesture not defined / recognized */ 128 | GEST_ID_MOVE_UP = 0x01, /*!< Gesture Move Up */ 129 | GEST_ID_MOVE_RIGHT = 0x02, /*!< Gesture Move Right */ 130 | GEST_ID_MOVE_DOWN = 0x03, /*!< Gesture Move Down */ 131 | GEST_ID_MOVE_LEFT = 0x04, /*!< Gesture Move Left */ 132 | GEST_ID_ZOOM_IN = 0x05, /*!< Gesture Zoom In */ 133 | GEST_ID_ZOOM_OUT = 0x06, /*!< Gesture Zoom Out */ 134 | GEST_ID_NB_MAX = 0x07 /*!< max number of gesture id */ 135 | 136 | } TS_GestureIdTypeDef; 137 | 138 | /** 139 | * @brief TS_TouchEventTypeDef 140 | * Define Possible touch events kind as returned values 141 | * by touch screen IC Driver. 142 | */ 143 | typedef enum 144 | { 145 | TOUCH_EVENT_NO_EVT = 0x00, /*!< Touch Event : undetermined */ 146 | TOUCH_EVENT_PRESS_DOWN = 0x01, /*!< Touch Event Press Down */ 147 | TOUCH_EVENT_LIFT_UP = 0x02, /*!< Touch Event Lift Up */ 148 | TOUCH_EVENT_CONTACT = 0x03, /*!< Touch Event Contact */ 149 | TOUCH_EVENT_NB_MAX = 0x04 /*!< max number of touch events kind */ 150 | 151 | } TS_TouchEventTypeDef; 152 | /** 153 | * @} 154 | */ 155 | 156 | /** @defgroup STM32746G_DISCOVERY_TS_Imported_Variables STM32746G_DISCOVERY_TS Imported Variables 157 | * @{ 158 | */ 159 | /** 160 | * @brief Table for touchscreen event information display on LCD : 161 | * table indexed on enum @ref TS_TouchEventTypeDef information 162 | */ 163 | extern char * ts_event_string_tab[TOUCH_EVENT_NB_MAX]; 164 | 165 | /** 166 | * @brief Table for touchscreen gesture Id information display on LCD : table indexed 167 | * on enum @ref TS_GestureIdTypeDef information 168 | */ 169 | extern char * ts_gesture_id_string_tab[GEST_ID_NB_MAX]; 170 | /** 171 | * @} 172 | */ 173 | 174 | /** @addtogroup STM32746G_DISCOVERY_TS_Exported_Functions 175 | * @{ 176 | */ 177 | uint8_t BSP_TS_Init(uint16_t ts_SizeX, uint16_t ts_SizeY); 178 | uint8_t BSP_TS_DeInit(void); 179 | uint8_t BSP_TS_GetState(TS_StateTypeDef *TS_State); 180 | 181 | #if (TS_MULTI_TOUCH_SUPPORTED == 1) 182 | uint8_t BSP_TS_Get_GestureId(TS_StateTypeDef *TS_State); 183 | #endif /* TS_MULTI_TOUCH_SUPPORTED == 1 */ 184 | 185 | uint8_t BSP_TS_ITConfig(void); 186 | uint8_t BSP_TS_ITGetStatus(void); 187 | void BSP_TS_ITClear(void); 188 | uint8_t BSP_TS_ResetTouchData(TS_StateTypeDef *TS_State); 189 | 190 | /** 191 | * @} 192 | */ 193 | 194 | /** 195 | * @} 196 | */ 197 | 198 | /** 199 | * @} 200 | */ 201 | 202 | /** 203 | * @} 204 | */ 205 | 206 | 207 | #ifdef __cplusplus 208 | } 209 | #endif 210 | 211 | #endif /* __STM32746G_DISCOVERY_TS_H */ 212 | 213 | /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 214 | -------------------------------------------------------------------------------- /driver/stm32/STM32F7DISC/ts.h: -------------------------------------------------------------------------------- 1 | /** 2 | ****************************************************************************** 3 | * @file ts.h 4 | * @author MCD Application Team 5 | * @version V4.0.1 6 | * @date 21-July-2015 7 | * @brief This file contains all the functions prototypes for the Touch Screen driver. 8 | ****************************************************************************** 9 | * @attention 10 | * 11 | *

© COPYRIGHT(c) 2015 STMicroelectronics

12 | * 13 | * Redistribution and use in source and binary forms, with or without modification, 14 | * are permitted provided that the following conditions are met: 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 2. Redistributions in binary form must reproduce the above copyright notice, 18 | * this list of conditions and the following disclaimer in the documentation 19 | * and/or other materials provided with the distribution. 20 | * 3. Neither the name of STMicroelectronics nor the names of its contributors 21 | * may be used to endorse or promote products derived from this software 22 | * without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * 35 | ****************************************************************************** 36 | */ 37 | 38 | /* Define to prevent recursive inclusion -------------------------------------*/ 39 | #ifndef __TS_H 40 | #define __TS_H 41 | 42 | #ifdef __cplusplus 43 | extern "C" { 44 | #endif 45 | 46 | /* Includes ------------------------------------------------------------------*/ 47 | #include 48 | 49 | /** @addtogroup BSP 50 | * @{ 51 | */ 52 | 53 | /** @addtogroup Components 54 | * @{ 55 | */ 56 | 57 | /** @addtogroup TS 58 | * @{ 59 | */ 60 | 61 | /** @defgroup TS_Exported_Types 62 | * @{ 63 | */ 64 | 65 | /** @defgroup TS_Driver_structure Touch Sensor Driver structure 66 | * @{ 67 | */ 68 | typedef struct 69 | { 70 | void (*Init)(uint16_t); 71 | uint16_t (*ReadID)(uint16_t); 72 | void (*Reset)(uint16_t); 73 | void (*Start)(uint16_t); 74 | uint8_t (*DetectTouch)(uint16_t); 75 | void (*GetXY)(uint16_t, uint16_t*, uint16_t*); 76 | void (*EnableIT)(uint16_t); 77 | void (*ClearIT)(uint16_t); 78 | uint8_t (*GetITStatus)(uint16_t); 79 | void (*DisableIT)(uint16_t); 80 | }TS_DrvTypeDef; 81 | /** 82 | * @} 83 | */ 84 | 85 | /** 86 | * @} 87 | */ 88 | 89 | /** 90 | * @} 91 | */ 92 | 93 | /** 94 | * @} 95 | */ 96 | 97 | /** 98 | * @} 99 | */ 100 | 101 | #ifdef __cplusplus 102 | } 103 | #endif 104 | 105 | #endif /* __TS_H */ 106 | 107 | /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 108 | -------------------------------------------------------------------------------- /driver/stm32/string1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | char *strncpy(char * dest, const char * src, size_t n){ 5 | if (n != 0) { 6 | char *d = dest; 7 | const char *s = src; 8 | do { 9 | if ((*d++ = *s++) == '\0') { 10 | while (--n != 0) 11 | *d++ = '\0'; 12 | break; 13 | } 14 | } while (--n != 0); 15 | } 16 | return (dest); 17 | } 18 | -------------------------------------------------------------------------------- /driver/zephyr/README.md: -------------------------------------------------------------------------------- 1 | ## Zephyr driver for LV bindings project 2 | 3 | This driver allows Micropython with LVGL as an external C module to run as a Zephyr OS application; thus, all boards, display, and input drivers can be used from Zephyr's codebase. 4 | 5 | ### lv_micropython 6 | This driver is integrated for Micropython's Zephyr port build in the [lv_micropython project](https://github.com/lvgl/lv_micropython). Using the lv_micopython Zephyr port with LVGL requires: 7 | * specifying the display and input drivers in the your-board.prj and in the lvgl_driver.h 8 | * editing LV_TICK_CUSTOM_INCLUDE in lv_conf.h as stated below. 9 | 10 | ### Driver architecture 11 | This driver consist of three files: 12 | 13 | #### lvgl.c 14 | This file is taken from the Zephyr codebase and is adjusted for this use case. Display driver initialization is being done here. 15 | #### modlvzephyr.c 16 | This file provides a binding to the lv_init function, which needs to be called before using LVGL from Micropython. 17 | #### lvgl_driver.h 18 | In this file, the display driver and the input driver need to be defined. In addition, the display resolution has to be defined here. 19 | 20 | ### Building the driver 21 | LV bindings project needs to be included as stated in the main README. 22 | To integrate the Zephyr driver, the following lines have to be inserted in Micropython's Zephyr port: 23 | * Include path of Micropython's build systems have to be added to the Makefile: 24 | ``` 25 | INC += -I$(ZEPHYR_BASE) 26 | INC += -I$(TOP)/lib/lv_bindings 27 | INC += -I$(TOP)/lib/lv_bindings/driver/zephyr 28 | INC += -I$(ZEPHYR_BASE)/include 29 | INC += -I$(ZEPHYR_BASE)/lib/gui/lvgl/ 30 | INC += -I$(TOP)/lib/lv_bindings/lvgl 31 | ``` 32 | 33 | * Driver C files from Zephyr codebase need to be add for the build to the Makefile. 34 | ``` 35 | SRC_C += $(ZEPHYR_BASE)/lib/gui/lvgl/lvgl_display_16bit.c \ 36 | $(ZEPHYR_BASE)/lib/gui/lvgl/lvgl_display_24bit.c \ 37 | $(ZEPHYR_BASE)/lib/gui/lvgl/lvgl_display_32bit.c \ 38 | $(ZEPHYR_BASE)/lib/gui/lvgl/lvgl_display.c \ 39 | $(ZEPHYR_BASE)/lib/gui/lvgl/lvgl_display_mono.c \ 40 | build/lvgl/lv_mpy.c 41 | ``` 42 | * ```lvgl_driver.h``` has to be adjusted to specific board and display. 43 | * Display driver and input device driver need to be enabled in some project config, e.g., in your board .prj file. The following example is enabling the ELCDIF display driver and FT5336 input driver: 44 | ``` 45 | CONFIG_DISPLAY_MCUX_ELCDIF=y 46 | CONFIG_DISPLAY=y 47 | CONFIG_MCUX_ELCDIF_PANEL_RK043FN02H=y 48 | CONFIG_KSCAN=y 49 | CONFIG_KSCAN_FT5336=y 50 | ``` 51 | * Link Zephyr kernel with MPY app, adding following line to Zepjyr's port CMakelists.txt: 52 | ``` 53 | target_link_libraries(libmicropython INTERFACE kernel) 54 | ``` 55 | * edit lv_conf.h: 56 | ``` 57 | #define LV_TICK_CUSTOM_INCLUDE "kernel.h" 58 | #define LV_TICK_CUSTOM_SYS_TIME_EXPR (k_uptime_get_32()) 59 | 60 | typedef void *lv_disp_drv_user_data_t; 61 | typedef void *lv_indev_drv_user_data_t; 62 | 63 | ``` 64 | 65 | #### Usage 66 | LVGL and driver initialization is following: 67 | ``` 68 | import lvgl as lv 69 | import ZDD 70 | ZDD.init() 71 | ``` 72 | -------------------------------------------------------------------------------- /driver/zephyr/lvgl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 Jan Van Winkel 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * changes: 7 | * Zephyr's logging -> printf 8 | * CONFIG_LVGL_HOR_RES_MAX -> LV_HOR_RES_MAX 9 | * CONFIG_LVGL_VER_RES_MAX -> LV_VER_RES_MAX 10 | * added debug log 11 | * removed SYS_INIT for lvgl_init 12 | * lvgl_init no longer static 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "lvgl_display.h" 20 | #include "lvgl_driver.h" 21 | #include 22 | #ifdef CONFIG_LVGL_USE_FILESYSTEM 23 | #include "lvgl_fs.h" 24 | #endif 25 | #define CONFIG_LVGL_POINTER_KSCAN 1 26 | #ifdef CONFIG_LVGL_POINTER_KSCAN 27 | #include 28 | #endif 29 | #include LV_MEM_CUSTOM_INCLUDE 30 | 31 | #ifdef CONFIG_LVGL_BUFFER_ALLOC_STATIC 32 | 33 | lv_disp_buf_t disp_buf; 34 | 35 | #define BUFFER_SIZE (CONFIG_LVGL_BITS_PER_PIXEL * ((CONFIG_LVGL_VDB_SIZE * \ 36 | LV_HOR_RES_MAX * LV_VER_RES_MAX) / 100) / 8) 37 | 38 | #define NBR_PIXELS_IN_BUFFER (BUFFER_SIZE * 8 / CONFIG_LVGL_BITS_PER_PIXEL) 39 | 40 | /* NOTE: depending on chosen color depth buffer may be accessed using uint8_t *, 41 | * uint16_t * or uint32_t *, therefore buffer needs to be aligned accordingly to 42 | * prevent unaligned memory accesses. 43 | */ 44 | static uint8_t buf0[BUFFER_SIZE] __aligned(4); 45 | #ifdef CONFIG_LVGL_DOUBLE_VDB 46 | static uint8_t buf1[BUFFER_SIZE] __aligned(4); 47 | #endif 48 | 49 | #endif /* CONFIG_LVGL_BUFFER_ALLOC_STATIC */ 50 | 51 | #ifdef CONFIG_LVGL_BUFFER_ALLOC_STATIC 52 | 53 | static int lvgl_allocate_rendering_buffers(lv_disp_t *disp_drv) 54 | { 55 | struct display_capabilities cap; 56 | const struct device *display_dev = (const struct device *)disp_drv->user_data; 57 | int err = 0; 58 | 59 | display_get_capabilities(display_dev, &cap); 60 | 61 | if (cap.x_resolution <= LV_HOR_RES_MAX) { 62 | disp_drv->hor_res = cap.x_resolution; 63 | } else { 64 | printf("Horizontal resolution is larger than maximum"); 65 | err = -ENOTSUP; 66 | } 67 | 68 | if (cap.y_resolution <= LV_VER_RES_MAX) { 69 | disp_drv->ver_res = cap.y_resolution; 70 | } else { 71 | printf("Vertical resolution is larger than maximum"); 72 | err = -ENOTSUP; 73 | } 74 | 75 | disp_drv->buffer = &disp_buf; 76 | #ifdef CONFIG_LVGL_DOUBLE_VDB 77 | lv_disp_buf_init(disp_drv->buffer, &buf0, &buf1, NBR_PIXELS_IN_BUFFER); 78 | #else 79 | lv_disp_buf_init(disp_drv->buffer, &buf0, NULL, NBR_PIXELS_IN_BUFFER); 80 | #endif /* CONFIG_LVGL_DOUBLE_VDB */ 81 | 82 | return err; 83 | } 84 | 85 | #else 86 | 87 | static int lvgl_allocate_rendering_buffers(lv_disp_t *disp_drv) 88 | { 89 | void *buf0 = NULL; 90 | void *buf1 = NULL; 91 | uint16_t buf_nbr_pixels; 92 | uint32_t buf_size; 93 | struct display_capabilities cap; 94 | const struct device *display_dev = (const struct device *)disp_drv->user_data; 95 | 96 | display_get_capabilities(display_dev, &cap); 97 | 98 | disp_drv->hor_res = cap.x_resolution; 99 | disp_drv->ver_res = cap.y_resolution; 100 | 101 | buf_nbr_pixels = (CONFIG_LVGL_VDB_SIZE * disp_drv->hor_res * 102 | disp_drv->ver_res) / 100; 103 | /* one horizontal line is the minimum buffer requirement for lvgl */ 104 | if (buf_nbr_pixels < disp_drv->hor_res) { 105 | buf_nbr_pixels = disp_drv->hor_res; 106 | } 107 | 108 | switch (cap.current_pixel_format) { 109 | case PIXEL_FORMAT_ARGB_8888: 110 | buf_size = 4 * buf_nbr_pixels; 111 | break; 112 | case PIXEL_FORMAT_RGB_888: 113 | buf_size = 3 * buf_nbr_pixels; 114 | break; 115 | case PIXEL_FORMAT_RGB_565: 116 | buf_size = 2 * buf_nbr_pixels; 117 | break; 118 | case PIXEL_FORMAT_MONO01: 119 | case PIXEL_FORMAT_MONO10: 120 | buf_size = buf_nbr_pixels / 8; 121 | buf_size += (buf_nbr_pixels % 8) == 0 ? 0 : 1; 122 | break; 123 | default: 124 | return -ENOTSUP; 125 | } 126 | 127 | buf0 = LV_MEM_CUSTOM_ALLOC(buf_size); 128 | if (buf0 == NULL) { 129 | printf("Failed to allocate memory for rendering buffer"); 130 | return -ENOMEM; 131 | } 132 | 133 | #ifdef CONFIG_LVGL_DOUBLE_VDB 134 | buf1 = LV_MEM_CUSTOM_ALLOC(buf_size); 135 | if (buf1 == NULL) { 136 | LV_MEM_CUSTOM_FREE(buf0); 137 | printf("Failed to allocate memory for rendering buffer"); 138 | return -ENOMEM; 139 | } 140 | #endif 141 | 142 | disp_drv->buffer = LV_MEM_CUSTOM_ALLOC(sizeof(lv_disp_buf_t)); 143 | if (disp_drv->buffer == NULL) { 144 | LV_MEM_CUSTOM_FREE(buf0); 145 | LV_MEM_CUSTOM_FREE(buf1); 146 | printf("Failed to allocate memory to store rendering buffers"); 147 | return -ENOMEM; 148 | } 149 | 150 | lv_disp_buf_init(disp_drv->buffer, buf0, buf1, buf_nbr_pixels); 151 | return 0; 152 | } 153 | #endif /* CONFIG_LVGL_BUFFER_ALLOC_STATIC */ 154 | 155 | #ifdef CONFIG_LVGL_POINTER_KSCAN 156 | K_MSGQ_DEFINE(kscan_msgq, sizeof(lv_indev_data_t), 157 | CONFIG_LVGL_POINTER_KSCAN_MSGQ_COUNT, 4); 158 | 159 | static void lvgl_pointer_kscan_callback(const struct device *dev, 160 | uint32_t row, 161 | uint32_t col, bool pressed) 162 | { 163 | lv_indev_data_t data = { 164 | .point.x = col, 165 | .point.y = row, 166 | .state = pressed ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL, 167 | }; 168 | int err = k_msgq_put(&kscan_msgq, &data, K_NO_WAIT); 169 | if ( err != 0) { 170 | printf("Could not put input data into queue, err: %d\n", err); 171 | } 172 | } 173 | 174 | static bool lvgl_pointer_kscan_read(lv_indev_t *drv, lv_indev_data_t *data) 175 | { 176 | lv_disp_t *disp; 177 | const struct device *disp_dev; 178 | struct display_capabilities cap; 179 | lv_indev_data_t curr; 180 | 181 | static lv_indev_data_t prev = { 182 | .point.x = 0, 183 | .point.y = 0, 184 | .state = LV_INDEV_STATE_REL, 185 | }; 186 | 187 | if (k_msgq_get(&kscan_msgq, &curr, K_NO_WAIT) != 0) { 188 | goto set_and_release; 189 | } 190 | 191 | prev = curr; 192 | 193 | disp = lv_disp_get_default(); 194 | disp_dev = disp->driver.user_data; 195 | 196 | display_get_capabilities(disp_dev, &cap); 197 | 198 | /* adjust kscan coordinates */ 199 | if (IS_ENABLED(CONFIG_LVGL_POINTER_KSCAN_SWAP_XY)) { 200 | lv_coord_t x; 201 | 202 | x = prev.point.x; 203 | prev.point.x = prev.point.y; 204 | prev.point.y = x; 205 | } 206 | 207 | if (IS_ENABLED(CONFIG_LVGL_POINTER_KSCAN_INVERT_X)) { 208 | if (cap.current_orientation == DISPLAY_ORIENTATION_NORMAL || 209 | cap.current_orientation == DISPLAY_ORIENTATION_ROTATED_180) { 210 | prev.point.x = cap.x_resolution - prev.point.x; 211 | } else { 212 | prev.point.x = cap.y_resolution - prev.point.x; 213 | } 214 | } 215 | 216 | if (IS_ENABLED(CONFIG_LVGL_POINTER_KSCAN_INVERT_Y)) { 217 | if (cap.current_orientation == DISPLAY_ORIENTATION_NORMAL || 218 | cap.current_orientation == DISPLAY_ORIENTATION_ROTATED_180) { 219 | prev.point.y = cap.y_resolution - prev.point.y; 220 | } else { 221 | prev.point.y = cap.x_resolution - prev.point.y; 222 | } 223 | } 224 | 225 | /* rotate touch point to match display rotation */ 226 | if (cap.current_orientation == DISPLAY_ORIENTATION_ROTATED_90) { 227 | lv_coord_t x; 228 | 229 | x = prev.point.x; 230 | prev.point.x = prev.point.y; 231 | prev.point.y = cap.y_resolution - x; 232 | } else if (cap.current_orientation == DISPLAY_ORIENTATION_ROTATED_180) { 233 | prev.point.x = cap.x_resolution - prev.point.x; 234 | prev.point.y = cap.y_resolution - prev.point.y; 235 | } else if (cap.current_orientation == DISPLAY_ORIENTATION_ROTATED_270) { 236 | lv_coord_t x; 237 | 238 | x = prev.point.x; 239 | prev.point.x = cap.x_resolution - prev.point.y; 240 | prev.point.y = x; 241 | } 242 | 243 | set_and_release: 244 | *data = prev; 245 | 246 | return k_msgq_num_used_get(&kscan_msgq) > 0; 247 | } 248 | 249 | static int lvgl_pointer_kscan_init(void) 250 | { 251 | const struct device *kscan_dev = 252 | device_get_binding(CONFIG_LVGL_POINTER_KSCAN_DEV_NAME); 253 | 254 | lv_indev_t indev_drv; 255 | 256 | if (kscan_dev == NULL) { 257 | printf("Keyboard scan device not found."); 258 | return -ENODEV; 259 | } 260 | 261 | if (kscan_config(kscan_dev, lvgl_pointer_kscan_callback) < 0) { 262 | printf("Could not configure keyboard scan device."); 263 | return -ENODEV; 264 | } 265 | 266 | lv_indev_drv_init(&indev_drv); 267 | indev_drv.type = LV_INDEV_TYPE_POINTER; 268 | indev_drv.read_cb = lvgl_pointer_kscan_read; 269 | 270 | if (lv_indev_drv_register(&indev_drv) == NULL) { 271 | printf("Failed to register input device."); 272 | return -EPERM; 273 | } 274 | 275 | int err = kscan_enable_callback(kscan_dev); 276 | if (err != 0) { 277 | printf("Callback enabling failed\n"); 278 | return err; 279 | } 280 | 281 | return 0; 282 | } 283 | #endif /* CONFIG_LVGL_POINTER_KSCAN */ 284 | 285 | int lvgl_init(const struct device *dev) 286 | { 287 | ARG_UNUSED(dev); 288 | 289 | const struct device *display_dev = 290 | device_get_binding(CONFIG_LVGL_DISPLAY_DEV_NAME); 291 | int err = 0; 292 | lv_disp_t disp_drv; 293 | 294 | if (display_dev == NULL) { 295 | printf("Display device not found."); 296 | return -ENODEV; 297 | } 298 | 299 | #if CONFIG_LVGL_LOG_LEVEL != 0 300 | lv_log_register_print_cb(lvgl_log); 301 | #endif 302 | 303 | lv_init(); 304 | 305 | #ifdef CONFIG_LVGL_USE_FILESYSTEM 306 | lvgl_fs_init(); 307 | #endif 308 | 309 | lv_disp_drv_init(&disp_drv); 310 | disp_drv.user_data = (void *) display_dev; 311 | 312 | err = lvgl_allocate_rendering_buffers(&disp_drv); 313 | if (err != 0) { 314 | printf("Error allocate buffers\n"); 315 | return err; 316 | } 317 | 318 | if (set_lvgl_rendering_cb(&disp_drv) != 0) { 319 | printf("Display not supported.\n"); 320 | return -ENOTSUP; 321 | } 322 | 323 | if (lv_disp_drv_register(&disp_drv) == NULL) { 324 | printf("Failed to register display device.\n"); 325 | return -EPERM; 326 | } 327 | 328 | #ifdef CONFIG_LVGL_POINTER_KSCAN 329 | lvgl_pointer_kscan_init(); 330 | #endif /* CONFIG_LVGL_POINTER_KSCAN */ 331 | 332 | return 0; 333 | } 334 | -------------------------------------------------------------------------------- /driver/zephyr/lvgl_driver.h: -------------------------------------------------------------------------------- 1 | #define CONFIG_LVGL_BITS_PER_PIXEL 32 2 | #define CONFIG_LVGL_VDB_SIZE 16 3 | #define CONFIG_LVGL_DISPLAY_DEV_NAME "ELCDIF_1" // choose from Zephyr display drivers 4 | #define CONFIG_LVGL_BUFFER_ALLOC_STATIC 1 // LVGL buffer settings, see other options in Zephyr's Kconfig 5 | #define CONFIG_LVGL_POINTER_KSCAN 1 // enabling/disabling kernel scan for input queue 6 | #define CONFIG_LVGL_POINTER_KSCAN_MSGQ_COUNT 10 7 | #define CONFIG_LVGL_POINTER_KSCAN_DEV_NAME "FT5336" // choose from Zephyr input drivers 8 | 9 | 10 | int lvgl_init(const struct device *dev); 11 | -------------------------------------------------------------------------------- /driver/zephyr/modlvzephyr.c: -------------------------------------------------------------------------------- 1 | #include "py/runtime.h" 2 | #include "lvgl_driver.h" 3 | 4 | STATIC mp_obj_t init() { 5 | lvgl_init(NULL); 6 | return mp_const_none; 7 | } 8 | STATIC MP_DEFINE_CONST_FUN_OBJ_0(init_obj, init); 9 | 10 | STATIC const mp_rom_map_elem_t ZDD_module_globals_table[] = { 11 | { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ZDD) }, 12 | { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&init_obj) }, 13 | }; 14 | STATIC MP_DEFINE_CONST_DICT(ZDD_module_globals, ZDD_module_globals_table); 15 | 16 | const mp_obj_module_t ZDD_user_cmodule = { 17 | .base = { &mp_type_module }, 18 | .globals = (mp_obj_dict_t*)&ZDD_module_globals, 19 | }; 20 | 21 | MP_REGISTER_MODULE(MP_QSTR_ZDD, ZDD_user_cmodule, 1); 22 | -------------------------------------------------------------------------------- /examples/Dynamic_loading_font_example.py: -------------------------------------------------------------------------------- 1 | # 2 | # Command line for running this example on the unix port from examples directory: 3 | # MICROPYPATH=./:../lib ../../../ports/unix/micropython -i Dynamic_loading_font_example.py 4 | # 5 | 6 | import usys as sys 7 | sys.path.append('') # See: https://github.com/micropython/micropython/issues/6419 8 | 9 | try: 10 | script_path = __file__[:__file__.rfind('/')] if __file__.find('/') >= 0 else '.' 11 | except NameError: 12 | script_path = '' 13 | 14 | import lvgl as lv 15 | import fs_driver 16 | 17 | lv.init() 18 | 19 | # display driver init... 20 | import display_driver_utils # Default implementation. Replace by your driver 21 | driver = display_driver_utils.driver() 22 | 23 | # FS driver init. 24 | 25 | fs_drv = lv.fs_drv_t() 26 | fs_driver.fs_register(fs_drv, 'S') 27 | 28 | ''' 29 |  load the font file from filesystem(For me is flash ) 30 |  How to convert font files refer here: https://github.com/lvgl/lv_font_conv 31 |  font-PHT-en-20.bin: 32 |    lv_font_conv --size 20 --format bin --bpp 1 --font Alibaba-PuHuiTi-Medium.subset.ttf --range 0x20-0x7f --no-compress -o font-PHT-en-20.bin 33 |  font-PHT-cn-20.bin: 34 |    lv_font_conv --size 20 --format bin --bpp 1 --font Alibaba-PuHuiTi-Medium.subset.ttf --range 0x4e00-0x4e56 --no-compress -o font-PHT-cn-20.bin 35 |  font-PHT-jp-20.bin: 36 |    lv_font_conv --size 20 --format bin --bpp 1 --font Alibaba-PuHuiTi-Medium.subset.ttf --range 0x3042-0x3093 --no-compress -o font-PHT-jp-20.bin 37 | ''' 38 | scr = lv.scr_act() 39 | scr.clean() 40 | 41 | myfont_cn = lv.font_load("S:%s/font/font-PHT-cn-20.bin" % script_path) 42 | 43 | label1 = lv.label(scr) 44 | label1.set_style_text_font(myfont_cn, 0) # set the font 45 | label1.set_text("上中下乎") 46 | label1.align(lv.ALIGN.CENTER, 0, -25) 47 | 48 | myfont_en = lv.font_load("S:%s/font/font-PHT-en-20.bin" % script_path) 49 | 50 | label2 = lv.label(scr) 51 | label2.set_style_text_font(myfont_en, 0) # set the font 52 | label2.set_text("Hello LVGL!") 53 | label2.align(lv.ALIGN.CENTER, 0, 25) 54 | 55 | 56 | myfont_jp = lv.font_load("S:%s/font/font-PHT-jp-20.bin" % script_path) 57 | 58 | label3 = lv.label(scr) 59 | label3.set_style_text_font(myfont_jp, 0) # set the font 60 | label3.set_text("こんにちはありがとう") 61 | label3.align(lv.ALIGN.CENTER, 0, 0) 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /examples/blue_flower_32.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdschlosser/lv_binding_micropython/6369315bb7bc3dc47f0b2cc33f7e7f8d830a5749/examples/blue_flower_32.bin -------------------------------------------------------------------------------- /examples/custom_widget_example.py: -------------------------------------------------------------------------------- 1 | 2 | # This example shows how to create a custom widget and a custom theme 3 | 4 | ############################################################################## 5 | # Initializations 6 | ############################################################################## 7 | 8 | import usys as sys 9 | sys.path.append('') # See: https://github.com/micropython/micropython/issues/6419 10 | 11 | import lvgl as lv 12 | import display_driver 13 | 14 | lv.init() 15 | 16 | ############################################################################## 17 | # Helper debug function to print member name 18 | ############################################################################## 19 | 20 | member_name_cache = {} 21 | 22 | def get_member_name(obj, value): 23 | try: 24 | return member_name_cache[id(obj)][id(value)] 25 | except KeyError: 26 | pass 27 | 28 | for member in dir(obj): 29 | if getattr(obj, member) == value: 30 | try: 31 | member_name_cache[id(obj)][id(value)] = member 32 | except KeyError: 33 | member_name_cache[id(obj)] = {id(value): member} 34 | return member 35 | 36 | ############################################################################## 37 | # A class that describes a custom widget class 38 | # An instance of this class can be used to create custom widgets 39 | ############################################################################## 40 | 41 | class CustomWidgetClass(): 42 | 43 | def __init__(self, width, height): 44 | # Define LVGL widget class 45 | # In this example the class receive parameters (width, height) 46 | self.lv_cls = lv.obj_class_t() 47 | self.lv_cls.constructor_cb = self.constructor 48 | self.lv_cls.destructor_cb = self.destructor 49 | self.lv_cls.event_cb = self.event_cb 50 | self.lv_cls.width_def = int(width) 51 | self.lv_cls.height_def = int(height) 52 | self.lv_cls.group_def = lv.obj.CLASS_GROUP_DEF.TRUE 53 | self.lv_cls.base_class = lv.obj_class 54 | 55 | def create(self, parent): 56 | # Create LVGL object from class 57 | return self.lv_cls.create_obj(parent) 58 | 59 | def get_class(self): 60 | # Return the internal LVGL class 61 | return self.lv_cls 62 | 63 | def constructor(self, lv_cls, obj): 64 | # Initialize the custom widget instance 65 | obj.valid = False 66 | obj.add_flag(obj.FLAG.CLICKABLE) 67 | obj.add_flag(obj.FLAG.CHECKABLE) 68 | obj.add_flag(obj.FLAG.SCROLL_ON_FOCUS) 69 | # print("Constructor called!") 70 | 71 | def destructor(self, lv_cls, obj): 72 | pass 73 | 74 | def event_cb(self, lv_cls, e): 75 | # Call the ancestor's event handler 76 | res = lv_cls.event_base(e) 77 | if res != lv.RES.OK: 78 | return 79 | 80 | code = e.get_code() 81 | obj = e.get_target_obj() 82 | 83 | # print("Event %s" % get_member_name(lv.EVENT, code)) 84 | 85 | if code == lv.EVENT.DRAW_MAIN: 86 | # Draw the widget 87 | draw_ctx = e.get_draw_ctx() 88 | self.draw(obj, draw_ctx) 89 | elif code in [ 90 | lv.EVENT.STYLE_CHANGED, 91 | lv.EVENT.VALUE_CHANGED, 92 | lv.EVENT.PRESSING, 93 | lv.EVENT.RELEASED]: 94 | # Check if need to recalculate widget parameters 95 | obj.valid = False 96 | 97 | def calc(self, obj): 98 | # Calculate object parameters 99 | area = lv.area_t() 100 | obj.get_content_coords(area) 101 | 102 | obj.draw_desc = lv.draw_rect_dsc_t() 103 | obj.draw_desc.init() 104 | obj.draw_desc.bg_opa = lv.OPA.COVER 105 | obj.draw_desc.bg_color = obj.get_style_bg_color(lv.PART.MAIN) 106 | 107 | obj.points = [ 108 | {'x':area.x1 + area.get_width()//2, 109 | 'y':area.y2 if obj.get_state() & lv.STATE.CHECKED else area.y1}, 110 | {'x':area.x2, 111 | 'y':area.y1 + area.get_height()//2}, 112 | {'x':area.x1, 113 | 'y':area.y1 + area.get_height()//2}, 114 | ] 115 | 116 | obj.valid = True 117 | 118 | def draw(self, obj, draw_ctx): 119 | # If object invalidated, recalculate its parameters 120 | if not obj.valid: 121 | self.calc(obj) 122 | 123 | # Draw the custom widget 124 | draw_ctx.polygon(obj.draw_desc, obj.points, len(obj.points)) 125 | 126 | ############################################################################## 127 | # A Python class to wrap the LVGL custom widget 128 | ############################################################################## 129 | 130 | class CustomWidget(): 131 | 132 | # An instance of a widget-class to be used for creating custom widgets 133 | d = lv.disp_get_default() 134 | dpi = d.get_dpi() 135 | cls = CustomWidgetClass(dpi, dpi) 136 | 137 | @staticmethod 138 | def get_class(): 139 | # Return the internal LVGL class 140 | return CustomWidget.cls.get_class() 141 | 142 | def __new__(cls, parent): 143 | # Return a new lv object instead of CustomWidget, 144 | # but first bind the LVGL object with CustomWidgetWrapper 145 | wrapper = cls.CustomWidgetWrapper(parent) 146 | return wrapper.lv_obj 147 | 148 | class CustomWidgetWrapper(): 149 | 150 | def __init__(self, parent): 151 | 152 | # Create the LVGL object from class 153 | self.lv_obj = CustomWidget.cls.create(parent) 154 | 155 | # Associates the LVGL object with CustomWidget wrapper 156 | self.lv_obj.set_user_data(self) 157 | 158 | # Initialize the object 159 | self.lv_obj.class_init_obj() 160 | 161 | 162 | def __getattr__(self, attr): 163 | # Provide access to LVGL object functions 164 | # print("__getattr__(%s, %s)" % (repr(self), repr(attr))) 165 | return getattr(self.lv_obj, attr) 166 | 167 | def __repr__(self): 168 | return "Custom Widget" 169 | 170 | ############################################################################## 171 | # A theme to apply styles to the custom widget 172 | ############################################################################## 173 | 174 | class CustomTheme(lv.theme_t): 175 | 176 | class Style(lv.style_t): 177 | def __init__(self): 178 | super().__init__() 179 | self.init() 180 | 181 | # Default color is gray 182 | self.set_bg_color(lv.palette_main(lv.PALETTE.GREY)) 183 | 184 | # Child elements are centered 185 | self.set_layout(lv.LAYOUT_FLEX.value) 186 | self.set_flex_main_place(lv.FLEX_ALIGN.CENTER) 187 | self.set_flex_cross_place(lv.FLEX_ALIGN.CENTER) 188 | self.set_flex_track_place(lv.FLEX_ALIGN.CENTER) 189 | 190 | class PressedStyle(lv.style_t): 191 | def __init__(self): 192 | super().__init__() 193 | self.init() 194 | 195 | # Pressed color is blue 196 | self.set_bg_color(lv.palette_main(lv.PALETTE.BLUE)) 197 | 198 | 199 | def __init__(self): 200 | super().__init__() 201 | self.custom_style = CustomTheme.Style() 202 | self.custom_pressed_style = CustomTheme.PressedStyle() 203 | 204 | # This theme is based on active theme 205 | base_theme = lv.theme_get_from_obj(lv.scr_act()) 206 | 207 | # This theme will be applied only after base theme is applied 208 | self.set_parent(base_theme) 209 | 210 | # Set the "apply" callback of this theme to a custom callback 211 | self.set_apply_cb(self.apply) 212 | 213 | # Activate this theme on the default display 214 | lv.disp_get_default().set_theme(self) 215 | 216 | def apply(self, theme, obj): 217 | # Apply this theme on CustomWidget class 218 | if obj.get_class() == CustomWidget.get_class(): 219 | obj.add_style(self.custom_style, lv.PART.MAIN) 220 | obj.add_style(self.custom_pressed_style, lv.PART.MAIN | lv.STATE.PRESSED) 221 | 222 | 223 | ############################################################################## 224 | # Main program - create screen and widgets 225 | ############################################################################## 226 | 227 | # Create the theme for the custom widget 228 | theme = CustomTheme() 229 | 230 | # Create a screen with flex layout 231 | scr = lv.scr_act() 232 | scr.set_flex_flow(lv.FLEX_FLOW.COLUMN) 233 | scr.set_flex_align(lv.FLEX_ALIGN.SPACE_EVENLY, lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.CENTER) 234 | 235 | # Add a button with a label 236 | btn = lv.btn(scr) 237 | l1 = lv.label(btn) 238 | l1.set_text("Hello!") 239 | 240 | # Add a custom widget with a label 241 | customWidget = CustomWidget(scr) 242 | l2 = lv.label(customWidget) 243 | l2.set_text("Click me!") 244 | 245 | # Add click events to both button and custom widget 246 | def event_cb(e): 247 | print("%s Clicked!" % repr(e.get_target_obj())) 248 | 249 | for widget in [btn, customWidget]: 250 | widget.add_event(event_cb, lv.EVENT.CLICKED, None) 251 | 252 | -------------------------------------------------------------------------------- /examples/example3.py: -------------------------------------------------------------------------------- 1 | import usys as sys 2 | sys.path.append('') # See: https://github.com/micropython/micropython/issues/6419 3 | 4 | import lv_utils 5 | import lvgl as lv 6 | 7 | try: 8 | import pyb 9 | import rk043fn48h as lcd 10 | 11 | hres = 480 12 | vres = 272 13 | lv.init() 14 | event_loop = lv_utils.event_loop() 15 | lcd.init(w=hres, h=vres) 16 | disp_drv = lv.disp_create(hres, vres) 17 | disp_drv.set_flush_cb(lcd.flush) 18 | buf1_1 = bytearray(hres * 10 * lv.color_t.__SIZE__) 19 | buf1_2 = bytearray(hres * 10 * lv.color_t.__SIZE__) 20 | disp_drv.set_draw_buffers(buf1_1, buf1_2, len(buf1_1), lv.DISP_RENDER_MODE.PARTIAL) 21 | 22 | # disp_drv.gpu_blend_cb = lcd.gpu_blend 23 | # disp_drv.gpu_fill_cb = lcd.gpu_fill 24 | 25 | indev_drv = lv.indev_create() 26 | indev_drv.set_type(lv.INDEV_TYPE.POINTER) 27 | indev_drv.set_read_cb(lcd.ts_read) 28 | 29 | except ImportError: 30 | import display_driver 31 | 32 | scr1 = lv.obj() 33 | scr2 = lv.obj() 34 | lv.scr_load(scr1) 35 | 36 | slider = lv.slider(scr2) 37 | slider.set_width(150) 38 | slider.align(lv.ALIGN.TOP_MID, 0, 15) 39 | 40 | btn1 = lv.btn(scr1) 41 | btn1.align(lv.ALIGN.TOP_RIGHT, -5, 5) 42 | label = lv.label(btn1) 43 | label.set_text(">") 44 | 45 | btn2 = lv.btn(scr2) 46 | btn2.align(lv.ALIGN.TOP_LEFT, 5, 5) 47 | label2 = lv.label(btn2) 48 | label2.set_text("<") 49 | 50 | led1 = lv.led(scr2) 51 | led1.align(lv.ALIGN.CENTER, 0, 0) 52 | led1.set_brightness(slider.get_value() * 2) 53 | # led1.set_drag(True) 54 | led1.set_size(20,20) 55 | 56 | 57 | def slider_event_cb(event): 58 | led1.set_brightness(slider.get_value() * 2) 59 | 60 | def btn1_event_cb(event): 61 | lv.scr_load(scr2) 62 | 63 | 64 | def btn2_event_cb(event): 65 | lv.scr_load(scr1) 66 | 67 | slider.add_event(slider_event_cb, lv.EVENT.VALUE_CHANGED, None) 68 | btn1.add_event(btn1_event_cb, lv.EVENT.CLICKED, None) 69 | btn2.add_event(btn2_event_cb, lv.EVENT.CLICKED, None) 70 | 71 | # Create a keyboard 72 | kb = lv.keyboard(scr1) 73 | # kb.set_cursor_manage(True) 74 | 75 | # Create a text area. The keyboard will write here 76 | ta = lv.textarea(scr1) 77 | ta.set_width(450) 78 | ta.set_height(70) 79 | ta.align(lv.ALIGN.CENTER, 0, 0) 80 | ta.set_text("") 81 | 82 | # Assign the text area to the keyboard 83 | kb.set_textarea(ta) 84 | 85 | # Create a Spinner object 86 | spin = lv.spinner(scr2, 1000, 100) 87 | spin.set_size(100, 100) 88 | spin.align(lv.ALIGN.CENTER, 0, 0) 89 | # spin.set_type(lv.spinner.TYPE.FILLSPIN_ARC) 90 | -------------------------------------------------------------------------------- /examples/fb_test.py: -------------------------------------------------------------------------------- 1 | # A simple test for Linux Frame Buffer 2 | # Registers FB display and mouse evdev and shows line cursor 3 | # then shows a button on screen. 4 | 5 | import lvgl as lv 6 | from lv_utils import event_loop 7 | import evdev 8 | 9 | lv.init() 10 | 11 | # Register FB display driver 12 | 13 | event_loop = event_loop() 14 | disp = lv.linux_fbdev_create() 15 | lv.linux_fbdev_set_file(disp, "/dev/fb0") 16 | 17 | # Register mouse device and crosshair cursor 18 | 19 | mouse = evdev.mouse_indev(lv.scr_act()) 20 | 21 | # Create a screen and a button 22 | 23 | btn = lv.btn(lv.scr_act()) 24 | btn.align(lv.ALIGN.CENTER, 0, 0) 25 | label = lv.label(btn) 26 | label.set_text("Hello World!") 27 | -------------------------------------------------------------------------------- /examples/font/font-PHT-cn-20.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdschlosser/lv_binding_micropython/6369315bb7bc3dc47f0b2cc33f7e7f8d830a5749/examples/font/font-PHT-cn-20.bin -------------------------------------------------------------------------------- /examples/font/font-PHT-en-20.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdschlosser/lv_binding_micropython/6369315bb7bc3dc47f0b2cc33f7e7f8d830a5749/examples/font/font-PHT-en-20.bin -------------------------------------------------------------------------------- /examples/font/font-PHT-jp-20.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdschlosser/lv_binding_micropython/6369315bb7bc3dc47f0b2cc33f7e7f8d830a5749/examples/font/font-PHT-jp-20.bin -------------------------------------------------------------------------------- /examples/generic-st77xx-with-xpt2046.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append('.') 3 | from st77xx import * 4 | from xpt2046 import * 5 | 6 | spi=machine.SPI( 7 | 1, 8 | baudrate=24_000_000, 9 | polarity=0, 10 | phase=0, 11 | sck=machine.Pin(10,machine.Pin.OUT), 12 | mosi=machine.Pin(11,machine.Pin.OUT), 13 | miso=machine.Pin(12,machine.Pin.IN) 14 | ) 15 | 16 | if 0: 17 | # with DMA, the repaints seem to be too slow? To be investigated 18 | # we seem to be fine performance-wise without DMA with 320x240 anyway 19 | import rp2_dma 20 | rp2_dma=rp2_dma.DMA(0) 21 | else: rp2_dma=None 22 | 23 | import lvgl as lv 24 | lv.init() 25 | 26 | lcd=St7789(rot=3,res=(240,320),spi=spi,cs=9,dc=8,bl=13,rst=15,rp2_dma=rp2_dma,factor=8) 27 | lcd.set_backlight(30) 28 | touch=Xpt2046(spi=spi,cs=16,rot=1,spiPrereadCb=lcd.rp2_wait_dma) 29 | 30 | scr=lv.obj() 31 | btn=lv.btn(scr) 32 | lbl=lv.label(btn) 33 | lbl.set_text("Press me!") 34 | btn.center() 35 | btn.add_event(lambda event: print('Button clicked!'),lv.EVENT.CLICKED,None) 36 | lv.scr_load(scr) 37 | -------------------------------------------------------------------------------- /examples/madctl/README.md: -------------------------------------------------------------------------------- 1 | # Determining the `colormode`, `invert`, and `rot` parameters 2 | 3 | The [madctl_helper.py](madctl_helper.py) program can help determine the `colormode`, `invert`, and 4 | `rot` parameters for a display. Set the `mosi`, `clk`, `dc`, `cs`, `backlight`, `width`, and 5 | `height` parameters for your display. Start with `rot=0`, `colormode=COLOR_MODE_RGB`, and `rot=0`. 6 | Run the program with the display in the orientation you would like to configure. 7 | 8 | ## Setting the `colormode` and `invert` parameter 9 | 10 | The program will draw a large red full-screen button with an 'F' character centered in the button. 11 | You can determine the `colormode` and `invert` parameters by observing the button's color. 12 | 13 | - If the button is RED, the `colormode` and `invert` parameters are correct. 14 | - If the button is BLUE, `colormode` should be `COLOR_MODE_BGR`. 15 | - If the button is YELLOW, `invert` should be `True`. 16 | - If the button is CYAN, `colormode` should be `COLOR_MODE_BGR` and `invert` should be `True`. 17 | 18 | ## Setting the `rot` parameter 19 | 20 | The `rot` parameter is used to set the MADCTL register of the display. The MADCTL register controls 21 | the order that pixels are written to the framebuffer. This sets the Orientation or Rotation of the 22 | display. Match the orientation and direction of the 'F' character as drawn on the display by the 23 | [madctl_helper.py](madctl_helper.py) program with the examples in the table below to determine the 24 | `rot` parameter value. 25 | 26 | Image | `rot` parameter 27 | ----- | --------------- 28 | ![MADCTL_0](images/madctl_0.png) | rot=0 29 | ![MADCTL_MY](images/madctl_y.png) | rot=MADCTL_MY 30 | ![MADCTL_MX](images/madctl_x.png) | rot=MADCTL_MX 31 | ![MADCTL_MX | MADCTL_MY](images/madctl_xy.png) | rot=MADCTL_MX | MADCTL_MY 32 | ![MADCTL_MV](images/madctl_v.png) | rot=MADCTL_MV 33 | ![MADCTL_MV | MADCTL_MY](images/madctl_vy.png) | rot=MADCTL_MV | MADCTL_MY 34 | ![MADCTL_MV | MADCTL_MX](images/madctl_vx.png) | rot=MADCTL_MV | MADCTL_MX 35 | ![MADCTL_MV | MADCTL_MX | MADCTL_MY](images/madctl_vxy.png) | rot=MADCTL_MV | MADCTL_MX | MADCTL_MX 36 | 37 | ## MADCTL Constants 38 | 39 | The following table shows the MADCTL bit flags and their effects. 40 | 41 | Constant | Value | Description 42 | -------------- | ----- | ---------------------- 43 | MADCTL_MY | 0x80 | Page Address Order 44 | MADCTL_MX | 0x40 | Column Address Order 45 | MADCTL_MV | 0x20 | Page/Column Order 46 | MADCTL_ML | 0x10 | Line Address Order 47 | MADCTL_MH | 0x04 | Display Data Latch Order 48 | COLOR_MODE_RGB | 0x00 | RGB color order 49 | COLOR_MODE_BGR | 0x08 | BGR color order 50 | -------------------------------------------------------------------------------- /examples/madctl/images/madctl_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdschlosser/lv_binding_micropython/6369315bb7bc3dc47f0b2cc33f7e7f8d830a5749/examples/madctl/images/madctl_0.png -------------------------------------------------------------------------------- /examples/madctl/images/madctl_v.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdschlosser/lv_binding_micropython/6369315bb7bc3dc47f0b2cc33f7e7f8d830a5749/examples/madctl/images/madctl_v.png -------------------------------------------------------------------------------- /examples/madctl/images/madctl_vx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdschlosser/lv_binding_micropython/6369315bb7bc3dc47f0b2cc33f7e7f8d830a5749/examples/madctl/images/madctl_vx.png -------------------------------------------------------------------------------- /examples/madctl/images/madctl_vxy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdschlosser/lv_binding_micropython/6369315bb7bc3dc47f0b2cc33f7e7f8d830a5749/examples/madctl/images/madctl_vxy.png -------------------------------------------------------------------------------- /examples/madctl/images/madctl_vy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdschlosser/lv_binding_micropython/6369315bb7bc3dc47f0b2cc33f7e7f8d830a5749/examples/madctl/images/madctl_vy.png -------------------------------------------------------------------------------- /examples/madctl/images/madctl_x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdschlosser/lv_binding_micropython/6369315bb7bc3dc47f0b2cc33f7e7f8d830a5749/examples/madctl/images/madctl_x.png -------------------------------------------------------------------------------- /examples/madctl/images/madctl_xy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdschlosser/lv_binding_micropython/6369315bb7bc3dc47f0b2cc33f7e7f8d830a5749/examples/madctl/images/madctl_xy.png -------------------------------------------------------------------------------- /examples/madctl/images/madctl_y.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdschlosser/lv_binding_micropython/6369315bb7bc3dc47f0b2cc33f7e7f8d830a5749/examples/madctl/images/madctl_y.png -------------------------------------------------------------------------------- /examples/madctl/madctl_helper.py: -------------------------------------------------------------------------------- 1 | import lvgl as lv 2 | from ili9XXX import * 3 | 4 | # ili9341 example 5 | disp = ili9341( 6 | mosi=18, clk=19, cs=13, dc=12, rst=4, backlight=15, backlight_on=1, 7 | width=128, height=160, colormode=COLOR_MODE_RGB, invert=False, rot=0) 8 | 9 | # st7789 example 10 | # disp = st7789( 11 | # mosi=19, clk=18, cs=5, dc=16, rst=23, backlight=4, backlight_on=1, 12 | # width=240, height=320, colormode=COLOR_MODE_RGB, invert=False, rot=0) 13 | 14 | style = lv.style_t() 15 | style.init() 16 | style.set_bg_color(lv.palette_main(lv.PALETTE.RED)) 17 | 18 | btn = lv.btn(lv.scr_act()) 19 | btn.set_size(disp.width, disp.height) 20 | btn.align(lv.ALIGN.CENTER,0,0) 21 | btn.add_style(style, 0) 22 | 23 | label = lv.label(btn) 24 | label.set_text("F") 25 | label.center() -------------------------------------------------------------------------------- /examples/png_decoder_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdschlosser/lv_binding_micropython/6369315bb7bc3dc47f0b2cc33f7e7f8d830a5749/examples/png_decoder_test.png -------------------------------------------------------------------------------- /examples/png_example.py: -------------------------------------------------------------------------------- 1 | ############################################################################## 2 | # 3 | # Example of the PNG image decoder Usage. 4 | # For dragging to work reasonable, make sure LV_IMG_CACHE_DEF_SIZE is not 0! 5 | # 6 | ############################################################################## 7 | 8 | import usys as sys 9 | sys.path.append('') # See: https://github.com/micropython/micropython/issues/6419 10 | 11 | import lvgl as lv 12 | import display_driver_utils 13 | 14 | # Init 15 | 16 | lv.init() 17 | driver = display_driver_utils.driver() 18 | 19 | scr = lv.scr_act() 20 | lv.img.cache_set_size(2) 21 | try: 22 | script_path = __file__[:__file__.rfind('/')] if __file__.find('/') >= 0 else '.' 23 | except NameError: 24 | script_path = '' 25 | 26 | # Load the image 27 | 28 | with open('%s/png_decoder_test.png' % script_path, 'rb') as f: 29 | png_data = f.read() 30 | 31 | png_img_dsc = lv.img_dsc_t({ 32 | 'data_size': len(png_data), 33 | 'data': png_data 34 | }) 35 | 36 | # Create an image using the decoder 37 | 38 | img1 = lv.img(scr) 39 | img1.set_src(png_img_dsc) 40 | img1.set_pos(100,50) 41 | 42 | # Create an image from a symbol 43 | 44 | img2 = lv.img(scr) 45 | img2.set_src(lv.SYMBOL.OK + " Accept") 46 | img2.set_pos(100,200) 47 | 48 | # Drag handler 49 | 50 | def drag_event_handler(e): 51 | self = e.get_target_obj() 52 | indev = lv.indev_get_act() 53 | vect = lv.point_t() 54 | indev.get_vect(vect) 55 | x = self.get_x() + vect.x 56 | y = self.get_y() + vect.y 57 | self.set_pos(x, y) 58 | 59 | # Register drag handler for images 60 | 61 | for img in [img1, img2]: 62 | img.add_flag(img.FLAG.CLICKABLE) 63 | img.add_event(drag_event_handler, lv.EVENT.PRESSING, None) 64 | 65 | 66 | -------------------------------------------------------------------------------- /examples/uasyncio_example1.py: -------------------------------------------------------------------------------- 1 | ################################################################################################## 2 | # 3 | # Example of LVGL with uasyncio event loop on unix and esp32 ports 4 | # 5 | # Running on Linux: (need micropython-dev for uasyncio): 6 | # ports/unix/micropython-dev -i lib/lv_bindings/examples/uasyncio_example1.py 7 | # 8 | # Important differences from non-uasyncio examples: 9 | # 10 | # - Import lv_utils and call lv_utils.event_loop (optional for ili9XXX) 11 | # - Pass asynchronous=True 12 | # - Call uasyncio.Loop.run_forever() 13 | # 14 | ################################################################################################## 15 | 16 | # Workaround for including frozen modules when running micropython with a script argument 17 | # https://github.com/micropython/micropython/issues/6419 18 | import usys as sys 19 | sys.path.append('') 20 | 21 | # Imports 22 | 23 | from urandom import getrandbits, seed 24 | from utime import ticks_us 25 | from uasyncio import sleep, create_task, Loop, CancelledError 26 | import lv_utils 27 | import lvgl as lv 28 | 29 | seed(ticks_us()) 30 | lv.init() 31 | 32 | ################################################################################################## 33 | # Display initialization 34 | ################################################################################################## 35 | 36 | # Default resolution match ili9341 37 | HOR_RES = 240 38 | VER_RES = 320 39 | 40 | # Try to initialize SDL 41 | try: 42 | # Register SDL display driver. 43 | 44 | disp_drv = lv.sdl_window_create(HOR_RES, VER_RES) 45 | mouse = lv.sdl_mouse_create() 46 | 47 | event_loop = lv_utils.event_loop(asynchronous=True) 48 | 49 | except AttributeError: 50 | pass 51 | 52 | # Try initialize ili9341/xpt2046 53 | try: 54 | from ili9XXX import ili9341 55 | from xpt2046 import xpt2046 56 | 57 | # Initialize and register drivers 58 | 59 | disp = ili9341(dc=32, cs=33, power=-1, backlight=-1, asynchronous=True, initialize=True) 60 | touch = xpt2046() 61 | 62 | except ImportError: 63 | pass 64 | 65 | ################################################################################################## 66 | # Stylized Message Box class 67 | ################################################################################################## 68 | 69 | class MsgBox(lv.win): 70 | 71 | def drag_event_handler(self, e): 72 | self.move_foreground() 73 | indev = lv.indev_get_act() 74 | indev.get_vect(self.vect) 75 | x = self.get_x() + self.vect.x 76 | y = self.get_y() + self.vect.y 77 | self.set_pos(x, y) 78 | 79 | def __init__(self, parent): 80 | super().__init__(parent, 20) 81 | self.vect = lv.point_t() 82 | 83 | self.set_size(100,80) 84 | self.add_title("Pop") 85 | msg_box_close_btn = self.add_btn(lv.SYMBOL.CLOSE, 20) 86 | msg_box_close_btn.add_event(lambda e: self.close_msg_box(), lv.EVENT.RELEASED, None) 87 | 88 | header = self.get_header() 89 | header.set_style_bg_color(lv.color_hex3(0xFEE), lv.PART.MAIN) 90 | 91 | content = self.get_content() 92 | content.set_style_bg_color(lv.color_hex3(0xFFF), lv.PART.MAIN) 93 | 94 | self.set_style_border_width(4, lv.PART.MAIN) 95 | self.set_style_border_color(lv.color_hex3(0xF88), lv.PART.MAIN) 96 | self.set_style_shadow_color(lv.color_hex3(0x000), lv.PART.MAIN) 97 | self.set_style_shadow_opa(50, lv.PART.MAIN) 98 | self.set_style_shadow_width(20, lv.PART.MAIN) 99 | self.set_style_shadow_ofs_x(10, lv.PART.MAIN) 100 | self.set_style_shadow_ofs_y(10, lv.PART.MAIN) 101 | self.set_style_shadow_spread(0, lv.PART.MAIN) 102 | self.set_style_radius(10, lv.PART.MAIN) 103 | 104 | self.label = lv.label(content) 105 | 106 | for element in [content, header]: 107 | element.add_event(self.drag_event_handler, lv.EVENT.PRESSING, None) 108 | 109 | self.opened = True 110 | 111 | def is_open(self): 112 | return self.opened 113 | 114 | def close_msg_box(self): 115 | if self.is_open(): 116 | self.anim = lv.anim_t() 117 | self.anim.init() 118 | self.anim.set_var(self) 119 | self.anim.set_time(500) 120 | self.anim.set_values(lv.OPA.COVER,lv.OPA.TRANSP) 121 | self.anim.set_custom_exec_cb(lambda obj, val: 122 | self.set_style_opa(val, lv.PART.MAIN)) 123 | self.anim.set_path_cb(lv.anim_t.path_ease_in) 124 | self.anim.set_ready_cb(lambda a: self.del_async()) 125 | lv.anim_t.start(self.anim) 126 | self.opened = False 127 | 128 | def set_text(self, txt): 129 | 130 | # If the msg box is already closed, cancel the calling task 131 | if not self.is_open(): 132 | raise CancelledError() 133 | 134 | self.label.set_text(txt) 135 | 136 | ################################################################################################## 137 | # Async task 138 | ################################################################################################## 139 | 140 | async def btn_event_task(obj=None, event=-1): 141 | 142 | # Create and position the a new msg box 143 | 144 | msg_box = MsgBox(scr) 145 | msg_box.set_pos(getrandbits(7), 50 + getrandbits(7)) 146 | 147 | # Countdown 148 | 149 | for i in range(10, 0, -1): 150 | msg_box.set_text(str(i)) 151 | await sleep(1) 152 | 153 | # Close the msg box 154 | 155 | msg_box.close_msg_box() 156 | 157 | ################################################################################################## 158 | # Create objects and screen 159 | ################################################################################################## 160 | 161 | scr = lv.scr_act() 162 | btn = lv.btn(scr) 163 | btn.align(lv.ALIGN.TOP_MID, 0, 10) 164 | btn.add_event(lambda e: create_task(btn_event_task()), lv.EVENT.CLICKED, None) 165 | label = lv.label(btn) 166 | label.set_text('Click Me Again!') 167 | 168 | ################################################################################################## 169 | # Start event loop 170 | ################################################################################################## 171 | 172 | Loop.run_forever() 173 | 174 | -------------------------------------------------------------------------------- /gen/.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | lextab.py 3 | yacctab.py 4 | 5 | -------------------------------------------------------------------------------- /include/lv_mp_mem_custom_include.h: -------------------------------------------------------------------------------- 1 | #ifndef __LV_MP_MEM_CUSTOM_INCLUDE_H 2 | #define __LV_MP_MEM_CUSTOM_INCLUDE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #endif //__LV_MP_MEM_CUSTOM_INCLUDE_H 9 | -------------------------------------------------------------------------------- /include/lv_mp_root_pointers.h: -------------------------------------------------------------------------------- 1 | #ifndef __LV_MP_ROOT_POINTERS_H 2 | #define __LV_MP_ROOT_POINTERS_H 3 | 4 | #include 5 | #include "lvgl/lvgl.h" 6 | 7 | typedef struct lvgl_root_pointers_t { 8 | LV_ROOTS 9 | } lvgl_root_pointers_t; 10 | 11 | #endif //__LV_MP_ROOT_POINTERS_H -------------------------------------------------------------------------------- /lib/display_driver.py: -------------------------------------------------------------------------------- 1 | from display_driver_utils import driver 2 | drv = driver() 3 | -------------------------------------------------------------------------------- /lib/display_driver_utils.py: -------------------------------------------------------------------------------- 1 | import usys as sys 2 | sys.path.append('') # See: https://github.com/micropython/micropython/issues/6419 3 | 4 | import lvgl as lv 5 | 6 | try: 7 | import lv_utils 8 | lv_utils_available = True 9 | except: 10 | lv_utils_available = False 11 | 12 | ORIENT_LANDSCAPE = False 13 | ORIENT_PORTRAIT = True 14 | 15 | class driver: 16 | 17 | def __init__(self,width=420,height=320,orientation=ORIENT_PORTRAIT, asynchronous=False, exception_sink=None, defaultGroup=True): 18 | 19 | if not lv.is_initialized(): 20 | lv.init() 21 | 22 | self.group = lv.group_create() 23 | if defaultGroup: 24 | self.group.set_default() 25 | 26 | self.width = width 27 | self.height = height 28 | self.orientation = orientation 29 | self.asynchronous = asynchronous 30 | self.exception_sink = exception_sink 31 | self.disp = None 32 | self.touch = None 33 | self.type = None 34 | if not (lv_utils_available and lv_utils.event_loop.is_running()): 35 | self.init_gui() 36 | 37 | def init_gui_SDL(self): 38 | if lv_utils_available: 39 | self.event_loop = lv_utils.event_loop(asynchronous=self.asynchronous, exception_sink=self.exception_sink) 40 | 41 | lv.sdl_window_create(self.width, self.height) 42 | self.mouse = lv.sdl_mouse_create() 43 | self.keyboard = lv.sdl_keyboard_create() 44 | self.keyboard.set_group(self.group) 45 | self.type = "SDL" 46 | print("Running the SDL lvgl version") 47 | 48 | def init_gui_ili9341(self): 49 | 50 | # Initialize ILI9341 display 51 | 52 | from ili9XXX import ili9341,LANDSCAPE 53 | from xpt2046 import xpt2046 54 | import espidf as esp 55 | 56 | if lv_utils_available: 57 | self.event_loop = lv_utils.event_loop(asynchronous=self.asynchronous, exception_sink=self.exception_sink) 58 | 59 | if self.orientation == ORIENT_PORTRAIT: 60 | print ("Running the ili9341 lvgl version in portrait mode") 61 | 62 | # Initialize ILI9341 display in prtrait mode 63 | # the following are the settings for the Lolin tft 2.4 display 64 | # self.disp = ili9341(miso=19,mosi=23,clk=18, cs=26, dc=5, rst=-1, power=-1, backlight=-1, spihost=esp.VSPI_HOST) 65 | # self.touch = xpt2046(spihost=esp.VSPI_HOST,cal_x0=3751, cal_x1 = 210, cal_y0=3906, cal_y1 = 283, transpose=True) 66 | 67 | self.disp = ili9341() 68 | self.touch = xpt2046() 69 | 70 | elif self.orientation == ORIENT_LANDSCAPE: 71 | print ("Running the ili9341 lvgl version in landscape mode") 72 | # Initialize ILI9341 display 73 | # the following are the settings for the Lolin tft 2.4 display 74 | # self.disp = ili9341(miso=19,mosi=23,clk=18, cs=26, dc=5, rst=-1, power=-1, backlight=-1, backlight_on=0, 75 | # spihost=esp.VSPI_HOST, width=320, height=240, rot=LANDSCAPE) 76 | # self.touch = xpt2046(spihost=esp.VSPI_HOST,cal_x0=3799, cal_x1 = 353, cal_y0=220, cal_y1 = 3719, transpose=False) 77 | 78 | # Register xpt2046 touch driver 79 | self.disp = ili9341(width=320, height=240, rot=LANDSCAPE) 80 | self.touch = xpt2046(cal_x0=3799, cal_x1 = 353, cal_y0=220, cal_y1 = 3719,transpose = False) 81 | 82 | else: 83 | raise RuntimeError("Invalid orientation") 84 | 85 | self.type="ili9341" 86 | 87 | ''' 88 | # Register raw resistive touch driver (remove xpt2046 initialization first) 89 | import rtch 90 | touch = rtch.touch(xp = 32, yp = 33, xm = 25, ym = 26, touch_rail = 27, touch_sense = 33) 91 | touch.init() 92 | self.indev_drv = lv.indev_create() 93 | self.indev_drv.set_type(lv.INDEV_TYPE.POINTER) 94 | self.indev_drv.set_read_cb(touch.read) 95 | ''' 96 | 97 | def init_gui_twatch(self): 98 | 99 | import ttgo 100 | from axp_constants import AXP202_VBUS_VOL_ADC1,AXP202_VBUS_CUR_ADC1,AXP202_BATT_CUR_ADC1,AXP202_BATT_VOL_ADC1 101 | 102 | watch = ttgo.Watch() 103 | tft = watch.tft 104 | power = watch.pmu 105 | power.adc1Enable(AXP202_VBUS_VOL_ADC1 106 | | AXP202_VBUS_CUR_ADC1 107 | | AXP202_BATT_CUR_ADC1 108 | | AXP202_BATT_VOL_ADC1, True) 109 | watch.lvgl_begin() 110 | watch.tft.backlight_fade(100) 111 | 112 | self.type="t-watch" 113 | print("Running lvgl on the LilyGo t-watch 2020") 114 | 115 | def init_gui(self): 116 | 117 | # Identify platform and initialize it 118 | 119 | try: 120 | self.init_gui_twatch() 121 | return 122 | except: 123 | pass 124 | 125 | try: 126 | self.init_gui_ili9341() 127 | return 128 | except ImportError: 129 | pass 130 | 131 | try: 132 | self.init_gui_SDL() 133 | return 134 | except ImportError: 135 | pass 136 | 137 | raise RuntimeError("Could not find a suitable display driver!") 138 | 139 | -------------------------------------------------------------------------------- /lib/fs_driver.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Original author: mhepp(https://forum.lvgl.io/u/mhepp/summary) 3 | ''' 4 | 5 | import lvgl as lv 6 | import ustruct as struct 7 | 8 | def fs_open_cb(drv, path, mode): 9 | 10 | if mode == lv.FS_MODE.WR: 11 | p_mode = 'wb' 12 | elif mode == lv.FS_MODE.RD: 13 | p_mode = 'rb' 14 | elif mode == lv.FS_MODE.WR | lv.FS_MODE.RD: 15 | p_mode = 'rb+' 16 | else: 17 | raise RuntimeError("fs_open_callback() - open mode error, %s is invalid mode" % mode) 18 | 19 | try: 20 | f = open(path, p_mode) 21 | 22 | except OSError as e: 23 | raise RuntimeError("fs_open_callback(%s) exception: %s" % (path, e)) 24 | 25 | return {'file' : f, 'path': path} 26 | 27 | 28 | def fs_close_cb(drv, fs_file): 29 | try: 30 | fs_file.__cast__()['file'].close() 31 | except OSError as e: 32 | raise RuntimeError("fs_close_callback(%s) exception: %s" % (fs_file.__cast__()['path'], e)) 33 | 34 | return lv.FS_RES.OK 35 | 36 | 37 | def fs_read_cb(drv, fs_file, buf, btr, br): 38 | try: 39 | tmp_data = fs_file.__cast__()['file'].read(btr) 40 | buf.__dereference__(btr)[0:len(tmp_data)] = tmp_data 41 | br.__dereference__(4)[0:4] = struct.pack("= 0: 89 | fs_drv.cache_size = cache_size 90 | 91 | fs_drv.register() 92 | 93 | -------------------------------------------------------------------------------- /lib/lv_colors.py: -------------------------------------------------------------------------------- 1 | import lvgl as lv 2 | def LV_COLOR_MAKE(r,g,b): 3 | # return lv.color_hex(r<<16| g<<8 |b) 4 | return lv.color_make(r,g,b) 5 | 6 | class lv_colors: 7 | WHITE=LV_COLOR_MAKE(0xFF, 0xFF, 0xFF) 8 | SILVER=LV_COLOR_MAKE(0xC0, 0xC0, 0xC0) 9 | GRAY=LV_COLOR_MAKE(0x80, 0x80, 0x80) 10 | BLACK=LV_COLOR_MAKE(0x00, 0x00, 0x00) 11 | RED=LV_COLOR_MAKE(0xFF, 0x00, 0x00) 12 | MAROON=LV_COLOR_MAKE(0x80, 0x00, 0x00) 13 | YELLOW=LV_COLOR_MAKE(0xFF, 0xFF, 0x00) 14 | OLIVE=LV_COLOR_MAKE(0x80, 0x80, 0x00) 15 | LIME=LV_COLOR_MAKE(0x00, 0xFF, 0x00) 16 | GREEN=LV_COLOR_MAKE(0x00, 0x80, 0x00) 17 | CYAN=LV_COLOR_MAKE(0x00, 0xFF, 0xFF) 18 | AQUA=CYAN 19 | TEAL=LV_COLOR_MAKE(0x00, 0x80, 0x80) 20 | BLUE=LV_COLOR_MAKE(0x00, 0x00, 0xFF) 21 | NAVY=LV_COLOR_MAKE(0x00, 0x00, 0x80) 22 | MAGENTA=LV_COLOR_MAKE(0xFF, 0x00, 0xFF) 23 | PURPLE=LV_COLOR_MAKE(0x80, 0x00, 0x80) 24 | ORANGE=LV_COLOR_MAKE(0xFF, 0xA5, 0x00) 25 | -------------------------------------------------------------------------------- /lib/lv_utils.py: -------------------------------------------------------------------------------- 1 | ############################################################################## 2 | # Event Loop module: advancing tick count and scheduling lvgl task handler. 3 | # Import after lvgl module. 4 | # This should be imported and used by display driver. 5 | # Display driver should first check if an event loop is already running. 6 | # 7 | # Usage example with SDL: 8 | # 9 | # SDL.init(auto_refresh=False) 10 | # # Register SDL display driver. 11 | # # Register SDL mouse driver 12 | # event_loop = lv_utils.event_loop() 13 | # 14 | # 15 | # uasyncio example with SDL: 16 | # 17 | # SDL.init(auto_refresh=False) 18 | # # Register SDL display driver. 19 | # # Register SDL mouse driver 20 | # event_loop = lv_utils.event_loop(asynchronous=True) 21 | # uasyncio.Loop.run_forever() 22 | # 23 | # uasyncio example with ili9341: 24 | # 25 | # event_loop = lv_utils.event_loop(asynchronous=True) # Optional! 26 | # self.disp = ili9341(asynchronous=True) 27 | # uasyncio.Loop.run_forever() 28 | # 29 | # MIT license; Copyright (c) 2021 Amir Gonnen 30 | # 31 | ############################################################################## 32 | 33 | import lvgl as lv 34 | import micropython 35 | import usys 36 | 37 | # Try standard machine.Timer, or custom timer from lv_timer, if available 38 | 39 | try: 40 | from machine import Timer 41 | except: 42 | try: 43 | from lv_timer import Timer 44 | except: 45 | raise RuntimeError("Missing machine.Timer implementation!") 46 | 47 | # Try to determine default timer id 48 | 49 | default_timer_id = 0 50 | if usys.platform == 'pyboard': 51 | # stm32 only supports SW timer -1 52 | default_timer_id = -1 53 | 54 | if usys.platform == 'rp2': 55 | # rp2 only supports SW timer -1 56 | default_timer_id = -1 57 | 58 | # Try importing uasyncio, if available 59 | 60 | try: 61 | import uasyncio 62 | uasyncio_available = True 63 | except: 64 | uasyncio_available = False 65 | 66 | ############################################################################## 67 | 68 | class event_loop(): 69 | 70 | _current_instance = None 71 | 72 | def __init__(self, freq=25, timer_id=default_timer_id, max_scheduled=2, refresh_cb=None, asynchronous=False, exception_sink=None): 73 | if self.is_running(): 74 | raise RuntimeError("Event loop is already running!") 75 | 76 | if not lv.is_initialized(): 77 | lv.init() 78 | 79 | event_loop._current_instance = self 80 | 81 | self.delay = 1000 // freq 82 | self.refresh_cb = refresh_cb 83 | self.exception_sink = exception_sink if exception_sink else self.default_exception_sink 84 | 85 | self.asynchronous = asynchronous 86 | if self.asynchronous: 87 | if not uasyncio_available: 88 | raise RuntimeError("Cannot run asynchronous event loop. uasyncio is not available!") 89 | self.refresh_event = uasyncio.Event() 90 | self.refresh_task = uasyncio.create_task(self.async_refresh()) 91 | self.timer_task = uasyncio.create_task(self.async_timer()) 92 | else: 93 | self.timer = Timer(timer_id) 94 | self.task_handler_ref = self.task_handler # Allocation occurs here 95 | self.timer.init(mode=Timer.PERIODIC, period=self.delay, callback=self.timer_cb) 96 | self.max_scheduled = max_scheduled 97 | self.scheduled = 0 98 | 99 | def deinit(self): 100 | if self.asynchronous: 101 | self.refresh_task.cancel() 102 | self.timer_task.cancel() 103 | else: 104 | self.timer.deinit() 105 | event_loop._current_instance = None 106 | 107 | def disable(self): 108 | self.scheduled += self.max_scheduled 109 | 110 | def enable(self): 111 | self.scheduled -= self.max_scheduled 112 | 113 | @staticmethod 114 | def is_running(): 115 | return event_loop._current_instance is not None 116 | 117 | @staticmethod 118 | def current_instance(): 119 | return event_loop._current_instance 120 | 121 | def task_handler(self, _): 122 | try: 123 | if lv._nesting.value == 0: 124 | lv.task_handler() 125 | if self.refresh_cb: self.refresh_cb() 126 | self.scheduled -= 1 127 | except Exception as e: 128 | if self.exception_sink: 129 | self.exception_sink(e) 130 | 131 | def timer_cb(self, t): 132 | # Can be called in Interrupt context 133 | # Use task_handler_ref since passing self.task_handler would cause allocation. 134 | lv.tick_inc(self.delay) 135 | if self.scheduled < self.max_scheduled: 136 | try: 137 | micropython.schedule(self.task_handler_ref, 0) 138 | self.scheduled += 1 139 | except: 140 | pass 141 | 142 | async def async_refresh(self): 143 | while True: 144 | await self.refresh_event.wait() 145 | if lv._nesting.value == 0: 146 | self.refresh_event.clear() 147 | try: 148 | lv.task_handler() 149 | except Exception as e: 150 | if self.exception_sink: 151 | self.exception_sink(e) 152 | if self.refresh_cb: self.refresh_cb() 153 | 154 | async def async_timer(self): 155 | while True: 156 | await uasyncio.sleep_ms(self.delay) 157 | lv.tick_inc(self.delay) 158 | self.refresh_event.set() 159 | 160 | 161 | def default_exception_sink(self, e): 162 | usys.print_exception(e) 163 | event_loop.current_instance().deinit() 164 | -------------------------------------------------------------------------------- /lib/tpcal.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append('') # See: https://github.com/micropython/micropython/issues/6419 3 | 4 | import lvgl as lv 5 | lv.init() 6 | # lv.log_register_print_cb(lambda msg: print(msg)) 7 | 8 | # Initialize ILI9341 display 9 | 10 | import espidf as esp 11 | from ili9XXX import ili9341,COLOR_MODE_BGR,LANDSCAPE,PORTRAIT 12 | 13 | disp = ili9341(dc=32, cs=33, power=-1, backlight=-1) 14 | 15 | # The call below is for a Lolin TFT-2.4 board 16 | # disp = ili9341(miso=19,mosi=23,clk=18, cs=26, dc=5, rst=-1, power=-1, backlight=-1, backlight_on=0, power_on=0, 17 | # spihost=esp.VSPI_HOST, mhz=40, factor=4, hybrid=True, width=240, height=320, 18 | # colormode=COLOR_MODE_BGR, rot=PORTRAIT, invert=False, double_buffer=True, half_duplex=True) 19 | 20 | # In order to calibrate in landscape mode please use: 21 | # disp = ili9341(rot=LANDSCAPE,width=320,height=240)) 22 | 23 | HRES = lv.scr_act().get_disp().driver.hor_res 24 | VRES = lv.scr_act().get_disp().driver.ver_res 25 | 26 | # Register xpt touch driver 27 | from xpt2046 import xpt2046 28 | touch = xpt2046(cal_x0=0, cal_x1 = HRES, cal_y0=0, cal_y1 = VRES) 29 | 30 | # The call below is for a Lolin TFT-2.4 board 31 | # touch = xpt2046(spihost=esp.VSPI_HOST,cal_x0=0, cal_x1 = HRES, cal_y0=0, cal_y1 = VRES, transpose=True) 32 | 33 | # In order to calibrate in landscape mode please use: 34 | # touch = xpt2046(cal_x0=0, cal_x1 = HRES, cal_y0=0, cal_y1 = VRES,transpose=False) 35 | 36 | # Point class, with both display and touch coordinates 37 | 38 | class Tpcal_point(): 39 | def __init__(self, x, y, name): 40 | self.display_coordinates = lv.point_t({'x': x, 'y': y}) 41 | self.touch_coordinate = None 42 | self.name = name 43 | 44 | def __repr__(self): 45 | return "%s: (%d, %d)" % (self.name, 46 | self.touch_coordinate.x, 47 | self.touch_coordinate.y) 48 | 49 | # Calibration helper class 50 | 51 | class Tpcal(): 52 | 53 | # Create a screen with a button and a label 54 | 55 | CIRCLE_SIZE = const(20) 56 | CIRCLE_OFFSET = const(20) 57 | TP_MAX_VALUE = const(10000) 58 | 59 | LV_COORD_MAX = const((1 << (8 * 2 - 1)) - 1000) 60 | LV_RADIUS_CIRCLE = const(LV_COORD_MAX) # TODO use lv.RADIUS_CIRCLE constant when it's available! 61 | 62 | def __init__(self, points, calibrate, touch_count = 5): 63 | 64 | self.points = points 65 | self.calibrate = calibrate 66 | self.touch_count = touch_count 67 | 68 | self.med = [lv.point_t() for i in range(0,self.touch_count)] # Storage point to calculate median 69 | 70 | self.cur_point = 0 71 | self.cur_touch = 0 72 | 73 | self.scr = lv.obj(None) 74 | self.scr.set_size(TP_MAX_VALUE, TP_MAX_VALUE) 75 | lv.scr_load(self.scr) 76 | 77 | # Create a big transparent button screen to receive clicks 78 | style_transp = lv.style_t() 79 | style_transp.init() 80 | style_transp.set_bg_opa(lv.OPA.TRANSP) 81 | self.big_btn = lv.btn(lv.scr_act()) 82 | self.big_btn.set_size(TP_MAX_VALUE, TP_MAX_VALUE) 83 | self.big_btn.add_style(style_transp, lv.PART.MAIN) 84 | self.big_btn.add_style(style_transp, lv.PART.MAIN) 85 | #self.big_btn.set_layout(lv.LAYOUT.OFF) 86 | self.big_btn.add_event(lambda event, self=self: self.calibrate_clicked(),lv.EVENT.CLICKED, None) 87 | 88 | # Create the screen, circle and label 89 | 90 | self.label_main = lv.label(lv.scr_act()) 91 | 92 | style_circ = lv.style_t() 93 | style_circ.init() 94 | style_circ.set_radius(LV_RADIUS_CIRCLE) 95 | 96 | self.circ_area = lv.obj(lv.scr_act()) 97 | self.circ_area.set_size(CIRCLE_SIZE, CIRCLE_SIZE) 98 | self.circ_area.add_style(style_circ, lv.STATE.DEFAULT) 99 | self.circ_area.clear_flag(lv.obj.FLAG.CLICKABLE) # self.circ_area.set_click(False) 100 | 101 | self.show_circle() 102 | 103 | def show_text(self, txt): 104 | self.label_main.set_text(txt) 105 | # self.label_main.set_align(lv.label.ALIGN.CENTER) 106 | self.label_main.set_pos((HRES - self.label_main.get_width() ) // 2, 107 | (VRES - self.label_main.get_height()) // 2) 108 | def show_circle(self): 109 | point = self.points[self.cur_point] 110 | self.show_text("Click the circle in\n" + \ 111 | point.name + "\n" + \ 112 | "%d left" % (self.touch_count - self.cur_touch)) 113 | if point.display_coordinates.x < 0: point.display_coordinates.x += HRES 114 | if point.display_coordinates.y < 0: point.display_coordinates.y += VRES 115 | # print("Circle coordinates: x: %d, y: %d"%(point.display_coordinates.x - self.CIRCLE_SIZE // 2, 116 | # point.display_coordinates.y - self.CIRCLE_SIZE // 2)) 117 | self.circ_area.set_pos(point.display_coordinates.x - CIRCLE_SIZE // 2, 118 | point.display_coordinates.y - CIRCLE_SIZE // 2) 119 | 120 | def calibrate_clicked(self): 121 | point = self.points[self.cur_point] 122 | indev = lv.indev_get_act() 123 | indev.get_point(self.med[self.cur_touch]) 124 | # print("calibrate_clicked: x: %d, y: %d"%(self.med[self.cur_touch].x,self.med[self.cur_touch].y)) 125 | self.cur_touch += 1 126 | 127 | if self.cur_touch == self.touch_count: 128 | med_x = sorted([med.x for med in self.med]) 129 | med_y = sorted([med.y for med in self.med]) 130 | x = med_x[len(med_x) // 2] 131 | y = med_y[len(med_y) // 2] 132 | point.touch_coordinate = lv.point_t({ 133 | 'x': x, 134 | 'y': y}) 135 | 136 | self.cur_point += 1 137 | self.cur_touch = 0 138 | 139 | if self.cur_point == len(self.points): 140 | self.calibrate(self.points) 141 | self.cur_point = 0 142 | self.show_text("Click/drag on screen\n" + \ 143 | "to check calibration") 144 | self.big_btn.set_event_cb(lambda event, self=self: self.check(), lv.EVENT.PRESSING, None) 145 | else: 146 | self.show_circle() 147 | 148 | def check(self): 149 | point = lv.point_t() 150 | indev = lv.indev_get_act() 151 | indev.get_point(point) 152 | # print("click position: x: %d, y: %d"%(point.x,point.y)) 153 | self.circ_area.set_pos(point.x - CIRCLE_SIZE // 2, 154 | point.y - CIRCLE_SIZE // 2) 155 | 156 | # Calculate calibration, and calibrate 157 | 158 | def calibrate(points): 159 | visual_width = points[1].display_coordinates.x - points[0].display_coordinates.x 160 | visual_height = points[1].display_coordinates.y - points[0].display_coordinates.y 161 | touch_width = points[1].touch_coordinate.x - points[0].touch_coordinate.x 162 | touch_height = points[1].touch_coordinate.y - points[0].touch_coordinate.y 163 | 164 | pixel_width = touch_width / visual_width 165 | pixel_height = touch_height / visual_height 166 | 167 | x0 = points[0].touch_coordinate.x - points[0].display_coordinates.x * pixel_width 168 | y0 = points[0].touch_coordinate.y - points[0].display_coordinates.y * pixel_height 169 | 170 | x1 = points[1].touch_coordinate.x + (HRES - points[1].display_coordinates.x) * pixel_width 171 | y1 = points[1].touch_coordinate.y + (VRES - points[1].display_coordinates.y) * pixel_height 172 | 173 | print("Calibration result: x0=%d, y0=%d, x1=%d, y1=%d" % (round(x0), round(y0), round(x1), round(y1))) 174 | touch.calibrate(round(x0), round(y0), round(x1), round(y1)) 175 | 176 | # Run calibration 177 | 178 | tpcal = Tpcal([ 179 | Tpcal_point(20, 20, "upper left-hand corner"), 180 | Tpcal_point(-40, -40, "lower right-hand corner"), 181 | ], calibrate) 182 | 183 | # while True: 184 | # pass 185 | -------------------------------------------------------------------------------- /lib/utils.py: -------------------------------------------------------------------------------- 1 | 2 | import lvgl as lv 3 | import ustruct 4 | import uctypes 5 | 6 | # Calculate pointer size on current machine, and corresponding fmt 7 | 8 | ptr_size = uctypes.sizeof({'p': (uctypes.PTR, uctypes.VOID)}) 9 | fmt_options = {2:'H', 4:'L', 8:'Q'} 10 | buf_fmt = fmt_options[ptr_size] if ptr_size in fmt_options else None 11 | 12 | 13 | def aligned_buf(buf, alignment): 14 | 15 | """Return an aligned buffer 16 | 17 | Given a buffer, return a memory view within that buffer, which starts 18 | at an aligned address in RAM. 19 | The returned memory view is possibly smaller. 20 | 21 | !! You must keep a reference to the original buffer to prevent the 22 | garbage collector from collecting the aligned view! 23 | 24 | Arguments: 25 | 26 | buf -- An object that implements buffer protocol 27 | alignment -- Integer value 28 | 29 | """ 30 | 31 | p = lv.C_Pointer() 32 | p.ptr_val = buf 33 | if not buf_fmt: return None 34 | addr = ustruct.unpack(buf_fmt, p.ptr_val)[0] 35 | mod = addr % alignment 36 | offset = alignment - mod if mod != 0 else 0 37 | if len(buf) <= offset: return None 38 | addr += offset 39 | p = lv.C_Pointer.__cast__(ustruct.pack(buf_fmt, addr)) 40 | return p.ptr_val.__dereference__(len(buf) - offset) 41 | 42 | -------------------------------------------------------------------------------- /mkrules.cmake: -------------------------------------------------------------------------------- 1 | 2 | find_package(Python3 REQUIRED COMPONENTS Interpreter) 3 | find_program(AWK awk mawk gawk) 4 | 5 | set(LV_BINDINGS_DIR ${CMAKE_CURRENT_LIST_DIR}) 6 | 7 | # Common function for creating LV bindings 8 | 9 | function(lv_bindings) 10 | set(_options) 11 | set(_one_value_args OUTPUT) 12 | set(_multi_value_args INPUT DEPENDS COMPILE_OPTIONS PP_OPTIONS GEN_OPTIONS FILTER) 13 | cmake_parse_arguments( 14 | PARSE_ARGV 0 LV 15 | "${_options}" 16 | "${_one_value_args}" 17 | "${_multi_value_args}" 18 | ) 19 | 20 | set(LV_PP ${LV_OUTPUT}.pp) 21 | set(LV_MPY_METADATA ${LV_OUTPUT}.json) 22 | 23 | add_custom_command( 24 | OUTPUT 25 | ${LV_PP} 26 | COMMAND 27 | ${CMAKE_C_COMPILER} -E -DPYCPARSER ${LV_COMPILE_OPTIONS} ${LV_PP_OPTIONS} "${LV_CFLAGS}" -I ${LV_BINDINGS_DIR}/pycparser/utils/fake_libc_include ${MICROPY_CPP_FLAGS} ${LV_INPUT} > ${LV_PP} 28 | DEPENDS 29 | ${LV_INPUT} 30 | ${LV_DEPENDS} 31 | ${LV_BINDINGS_DIR}/pycparser/utils/fake_libc_include 32 | IMPLICIT_DEPENDS 33 | C ${LV_INPUT} 34 | VERBATIM 35 | COMMAND_EXPAND_LISTS 36 | ) 37 | 38 | if(ESP_PLATFORM) 39 | target_compile_options(${COMPONENT_LIB} PRIVATE ${LV_COMPILE_OPTIONS}) 40 | else() 41 | target_compile_options(usermod_lv_bindings INTERFACE ${LV_COMPILE_OPTIONS}) 42 | endif() 43 | 44 | if (DEFINED LV_FILTER) 45 | 46 | set(LV_PP_FILTERED ${LV_PP}.filtered) 47 | set(LV_AWK_CONDITION) 48 | foreach(_f ${LV_FILTER}) 49 | string(APPEND LV_AWK_CONDITION "\$3!~\"${_f}\" && ") 50 | endforeach() 51 | string(APPEND LV_AWK_COMMAND "\$1==\"#\"{p=(${LV_AWK_CONDITION} 1)} p{print}") 52 | 53 | # message("AWK COMMAND: ${LV_AWK_COMMAND}") 54 | 55 | add_custom_command( 56 | OUTPUT 57 | ${LV_PP_FILTERED} 58 | COMMAND 59 | ${AWK} ${LV_AWK_COMMAND} ${LV_PP} > ${LV_PP_FILTERED} 60 | DEPENDS 61 | ${LV_PP} 62 | VERBATIM 63 | COMMAND_EXPAND_LISTS 64 | ) 65 | else() 66 | set(LV_PP_FILTERED ${LV_PP}) 67 | endif() 68 | 69 | add_custom_command( 70 | OUTPUT 71 | ${LV_OUTPUT} 72 | COMMAND 73 | ${Python3_EXECUTABLE} ${LV_BINDINGS_DIR}/gen/gen_mpy.py ${LV_GEN_OPTIONS} -MD ${LV_MPY_METADATA} -E ${LV_PP_FILTERED} ${LV_INPUT} > ${LV_OUTPUT} || (rm -f ${LV_OUTPUT} && /bin/false) 74 | DEPENDS 75 | ${LV_BINDINGS_DIR}/gen/gen_mpy.py 76 | ${LV_PP_FILTERED} 77 | COMMAND_EXPAND_LISTS 78 | ) 79 | 80 | endfunction() 81 | 82 | # Definitions for specific bindings 83 | 84 | set(LVGL_DIR ${LV_BINDINGS_DIR}/lvgl) 85 | 86 | set(LV_MP ${CMAKE_BINARY_DIR}/lv_mp.c) 87 | if(ESP_PLATFORM) 88 | set(LV_ESPIDF ${CMAKE_BINARY_DIR}/lv_espidf.c) 89 | endif() 90 | 91 | # Function for creating all specific bindings 92 | 93 | function(all_lv_bindings) 94 | 95 | # LVGL bindings 96 | 97 | file(GLOB_RECURSE LVGL_HEADERS ${LVGL_DIR}/src/*.h ${LV_BINDINGS_DIR}/lv_conf.h) 98 | lv_bindings( 99 | OUTPUT 100 | ${LV_MP} 101 | INPUT 102 | ${LVGL_DIR}/lvgl.h 103 | DEPENDS 104 | ${LVGL_HEADERS} 105 | GEN_OPTIONS 106 | -M lvgl -MP lv 107 | ) 108 | 109 | # ESPIDF bindings 110 | 111 | if(ESP_PLATFORM) 112 | file(GLOB_RECURSE LV_ESPIDF_HEADERS ${IDF_PATH}/components/*.h ${LV_BINDINGS_DIR}/driver/esp32/*.h) 113 | lv_bindings( 114 | OUTPUT 115 | ${LV_ESPIDF} 116 | INPUT 117 | ${LV_BINDINGS_DIR}/driver/esp32/espidf.h 118 | DEPENDS 119 | ${LV_ESPIDF_HEADERS} 120 | GEN_OPTIONS 121 | -M espidf 122 | FILTER 123 | i2s_ll.h 124 | i2s_hal.h 125 | esp_intr_alloc.h 126 | soc/spi_periph.h 127 | rom/ets_sys.h 128 | soc/sens_struct.h 129 | soc/rtc.h 130 | driver/periph_ctrl.h 131 | ) 132 | endif(ESP_PLATFORM) 133 | 134 | endfunction() 135 | 136 | # Add includes to CMake component 137 | 138 | set(LV_INCLUDE 139 | ${LV_BINDINGS_DIR} 140 | ) 141 | 142 | # Add sources to CMake component 143 | 144 | set(LV_SRC 145 | ${LV_MP} 146 | ) 147 | 148 | if(ESP_PLATFORM) 149 | LIST(APPEND LV_SRC 150 | ${LV_BINDINGS_DIR}/driver/esp32/espidf.c 151 | ${LV_BINDINGS_DIR}/driver/esp32/modrtch.c 152 | ${LV_BINDINGS_DIR}/driver/esp32/sh2lib.c 153 | ${LV_ESPIDF} 154 | ) 155 | endif(ESP_PLATFORM) 156 | -------------------------------------------------------------------------------- /tests/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e # Exit on error 4 | 5 | SCRIPT_PATH="`dirname \"$0\"`" 6 | 7 | NUMCPUS=`nproc` 8 | 9 | TEST_PATH=" \ 10 | $SCRIPT_PATH/../examples/*.py \ 11 | $SCRIPT_PATH/../lvgl/examples/ \ 12 | $SCRIPT_PATH/../lvgl/demos/ \ 13 | " 14 | 15 | EXCLUDE_PATH=" \ 16 | $SCRIPT_PATH/../examples/fb_test.py \ 17 | $SCRIPT_PATH/../examples/uasyncio*.py \ 18 | $SCRIPT_PATH/../examples/generic-st77xx-with-xpt2046.py \ 19 | $SCRIPT_PATH/../lvgl/demos/music/assets/spectrum.py \ 20 | " 21 | 22 | EXCLUDE_FINDEXP=$(echo $EXCLUDE_PATH | sed "s/^\|[[:space:]]/ -and -not -path /g") 23 | 24 | find $TEST_PATH -name "*.py" $EXCLUDE_FINDEXP |\ 25 | parallel --halt-on-error now,fail=1 --max-args=1 --max-procs $NUMCPUS -I {} timeout 5m catchsegv $SCRIPT_PATH/../../../ports/unix/build-standard/micropython $SCRIPT_PATH/run_test.py {} 26 | 27 | -------------------------------------------------------------------------------- /tests/run_test.py: -------------------------------------------------------------------------------- 1 | ############################################################################## 2 | # Run LVGL examples 3 | # An example script should be provided as an argument. 4 | # 5 | # This script would: 6 | # - Initialize LVGL and display 7 | # - Read the example file 8 | # - Chdir to the example file dir 9 | # - Run the example 10 | # - gc collect 11 | # - Try to perform some actions such as sending click/value-changed events 12 | # - Deinit the display 13 | # 14 | # It tries not to pollute the namespace of script under test. 15 | # The only import visible for it is 'lv'. 16 | # 17 | # In case of an error, display the error and exit with code 255 18 | # 255 was selected to stop xargs 19 | # 20 | ############################################################################## 21 | 22 | DELAY_MS=25 23 | 24 | import usys 25 | usys.path.append('') # See: https://github.com/micropython/micropython/issues/6419 26 | 27 | import gc 28 | import os 29 | import time 30 | import lvgl as lv 31 | import lv_utils 32 | import display_driver_utils 33 | 34 | events = [lv.EVENT.SCROLL, lv.EVENT.CLICKED, lv.EVENT.VALUE_CHANGED, lv.EVENT.READY, lv.EVENT.FOCUSED] 35 | 36 | class ExceptionHandler: 37 | def __init__(self): 38 | self.recorded_exception = None 39 | 40 | def handle_exceptions(self, e): 41 | lv_utils.event_loop.current_instance().deinit() 42 | if not self.recorded_exception: 43 | self.recorded_exception = e 44 | 45 | def reraise(self): 46 | if self.recorded_exception: 47 | raise self.recorded_exception 48 | 49 | lv.init() 50 | exception_handler = ExceptionHandler() 51 | driver = display_driver_utils.driver(exception_sink = exception_handler.handle_exceptions) 52 | scr = lv.scr_act() 53 | objects = [] 54 | 55 | def collect_objects(obj, user_data): 56 | if hasattr(obj, 'lv_obj'): 57 | obj = obj.lv_obj 58 | objects.append(obj) 59 | return lv.obj.TREE_WALK.NEXT 60 | 61 | def send_events(): 62 | for obj in objects: 63 | if lv.obj.__cast__(obj): # skip deleted objects 64 | obj_info = '' 65 | if hasattr(obj, 'get_text'): 66 | obj_info += ' text:"%s"' % obj.get_text() 67 | if hasattr(obj, 'get_value'): 68 | obj_info += ' value:"%s"' % obj.get_value() 69 | print('%s %s' % (obj, obj_info)) 70 | for event in events: 71 | if not lv.obj.__cast__(obj): # skip deleted objects 72 | continue 73 | # print('\t%s' % get_member_name(lv.EVENT, event)) 74 | obj.send_event(event, None) 75 | time.sleep_ms(DELAY_MS) 76 | gc.collect() 77 | 78 | def run(): 79 | try: 80 | script = usys.argv[1] 81 | script_path = script[:script.rfind('/')] if script.find('/') >= 0 else '.' 82 | script_name = script[script.rfind('/')+1:] if script.find('/') >= 0 else script 83 | 84 | print('Running %s ...' % script) 85 | 86 | with open(script, 'r') as file: 87 | file_string = file.read() 88 | os.chdir(script_path) 89 | usys.argv[0] = script_name 90 | del usys.argv[1] 91 | exec(file_string, {'__file__': script_name, 'lv': lv}) 92 | time.sleep_ms(DELAY_MS * 2) 93 | gc.collect() 94 | lv.scr_act().tree_walk(collect_objects, None) 95 | send_events() 96 | time.sleep_ms(DELAY_MS) 97 | exception_handler.reraise() 98 | if lv_utils.event_loop.is_running(): 99 | lv_utils.event_loop.current_instance().deinit() 100 | time.sleep_ms(DELAY_MS) 101 | 102 | except Exception as e: 103 | usys.print_exception(e) 104 | usys.exit(255) # 255 to exit xargs 105 | 106 | run() 107 | --------------------------------------------------------------------------------