mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
micropython: Refactor all display related modules.
This commit is contained in:
parent
7467731a38
commit
227824aaca
@ -173,9 +173,9 @@ MPY_CFLAGS += -DMICROPY_PY_MICRO_SPEECH=1
|
||||
MICROPY_ARGS += MICROPY_PY_MICRO_SPEECH=1
|
||||
endif
|
||||
|
||||
ifeq ($(MICROPY_PY_LCD), 1)
|
||||
MPY_CFLAGS += -DMICROPY_PY_LCD=1
|
||||
MICROPY_ARGS += MICROPY_PY_LCD=1
|
||||
ifeq ($(MICROPY_PY_DISPLAY), 1)
|
||||
MPY_CFLAGS += -DMICROPY_PY_DISPLAY=1
|
||||
MICROPY_ARGS += MICROPY_PY_DISPLAY=1
|
||||
endif
|
||||
|
||||
ifeq ($(MICROPY_PY_TV), 1)
|
||||
|
||||
246
src/omv/modules/py_display.c
Normal file
246
src/omv/modules/py_display.c
Normal file
@ -0,0 +1,246 @@
|
||||
/*
|
||||
* This file is part of the OpenMV project.
|
||||
*
|
||||
* Copyright (c) 2013-2023 Ibrahim Abdelkader <iabdalkader@openmv.io>
|
||||
* Copyright (c) 2013-2023 Kwabena W. Agyeman <kwagyeman@openmv.io>
|
||||
*
|
||||
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||
*
|
||||
* Display Python module.
|
||||
*/
|
||||
#include "omv_boardconfig.h"
|
||||
|
||||
#if MICROPY_PY_DISPLAY
|
||||
|
||||
#include "py/obj.h"
|
||||
#include "py/objarray.h"
|
||||
#include "py/mphal.h"
|
||||
#include "py/runtime.h"
|
||||
|
||||
#include "py_helper.h"
|
||||
#include "py_image.h"
|
||||
#include "py_display.h"
|
||||
|
||||
STATIC mp_obj_t py_display_width(mp_obj_t self_in) {
|
||||
py_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return mp_obj_new_int(self->width);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_display_width_obj, py_display_width);
|
||||
|
||||
STATIC mp_obj_t py_display_height(mp_obj_t self_in) {
|
||||
py_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return mp_obj_new_int(self->height);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_display_height_obj, py_display_height);
|
||||
|
||||
STATIC mp_obj_t py_display_triple_buffer(mp_obj_t self_in) {
|
||||
py_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return mp_obj_new_int(self->triple_buffer);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_display_triple_buffer_obj, py_display_triple_buffer);
|
||||
|
||||
STATIC mp_obj_t py_display_bgr(mp_obj_t self_in) {
|
||||
py_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return mp_obj_new_int(self->bgr);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_display_bgr_obj, py_display_bgr);
|
||||
|
||||
STATIC mp_obj_t py_display_byte_swap(mp_obj_t self_in) {
|
||||
py_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return mp_obj_new_int(self->byte_swap);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_display_byte_swap_obj, py_display_byte_swap);
|
||||
|
||||
STATIC mp_obj_t py_display_framesize(mp_obj_t self_in) {
|
||||
py_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return mp_obj_new_int(self->framesize);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_display_framesize_obj, py_display_framesize);
|
||||
|
||||
STATIC mp_obj_t py_display_refresh(mp_obj_t self_in) {
|
||||
py_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return mp_obj_new_int(self->refresh);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_display_refresh_obj, py_display_refresh);
|
||||
|
||||
STATIC mp_obj_t py_display_deinit(mp_obj_t self_in) {
|
||||
py_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
py_display_p_t *display_p = (py_display_p_t *) MP_OBJ_TYPE_GET_SLOT(self->base.type, protocol);
|
||||
if (display_p->deinit != NULL) {
|
||||
display_p->deinit(self);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_display_deinit_obj, py_display_deinit);
|
||||
|
||||
STATIC mp_obj_t py_display_clear(uint n_args, const mp_obj_t *args) {
|
||||
py_display_obj_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
bool display_off = (n_args > 1 && mp_obj_get_int(args[1]));
|
||||
py_display_p_t *display_p = (py_display_p_t *) MP_OBJ_TYPE_GET_SLOT(self->base.type, protocol);
|
||||
if (display_p->clear != NULL) {
|
||||
display_p->clear(self, display_off);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(py_display_clear_obj, 1, 2, py_display_clear);
|
||||
|
||||
STATIC mp_obj_t py_display_backlight(uint n_args, const mp_obj_t *args) {
|
||||
py_display_obj_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
|
||||
if (n_args > 1) {
|
||||
uint32_t intensity = mp_obj_get_int(args[1]);
|
||||
if ((intensity < 0) || (255 < intensity)) {
|
||||
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("0 <= intensity <= 255!"));
|
||||
}
|
||||
|
||||
py_display_p_t *display_p = (py_display_p_t *) MP_OBJ_TYPE_GET_SLOT(self->base.type, protocol);
|
||||
if (display_p->set_backlight != NULL) {
|
||||
display_p->set_backlight(self, intensity);
|
||||
}
|
||||
} else {
|
||||
return mp_obj_new_int(self->intensity);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(py_display_backlight_obj, 1, 2, py_display_backlight);
|
||||
|
||||
STATIC mp_obj_t py_display_write(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) {
|
||||
py_display_obj_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
image_t *arg_img = py_image_cobj(args[1]);
|
||||
|
||||
int arg_x_off = 0;
|
||||
py_helper_keyword_int_maybe(n_args, args, 2, kw_args,
|
||||
MP_OBJ_NEW_QSTR(MP_QSTR_x), &arg_x_off);
|
||||
|
||||
int arg_y_off = 0;
|
||||
py_helper_keyword_int_maybe(n_args, args, 3, kw_args,
|
||||
MP_OBJ_NEW_QSTR(MP_QSTR_y), &arg_y_off);
|
||||
|
||||
float arg_x_scale = 1.f;
|
||||
bool got_x_scale = py_helper_keyword_float_maybe(n_args, args, 4, kw_args,
|
||||
MP_OBJ_NEW_QSTR(MP_QSTR_x_scale), &arg_x_scale);
|
||||
|
||||
float arg_y_scale = 1.f;
|
||||
bool got_y_scale = py_helper_keyword_float_maybe(n_args, args, 5, kw_args,
|
||||
MP_OBJ_NEW_QSTR(MP_QSTR_y_scale), &arg_y_scale);
|
||||
|
||||
rectangle_t arg_roi;
|
||||
py_helper_keyword_rectangle_roi(arg_img, n_args, args, 6, kw_args, &arg_roi);
|
||||
|
||||
int arg_rgb_channel = py_helper_keyword_int(n_args, args, 7, kw_args,
|
||||
MP_OBJ_NEW_QSTR(MP_QSTR_rgb_channel), -1);
|
||||
if ((arg_rgb_channel < -1) || (2 < arg_rgb_channel)) {
|
||||
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("-1 <= rgb_channel <= 2!"));
|
||||
}
|
||||
|
||||
int arg_alpha = py_helper_keyword_int(n_args, args, 8, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_alpha), 256);
|
||||
if ((arg_alpha < 0) || (256 < arg_alpha)) {
|
||||
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("0 <= alpha <= 256!"));
|
||||
}
|
||||
|
||||
const uint16_t *color_palette = py_helper_keyword_color_palette(n_args, args, 7, kw_args, NULL);
|
||||
const uint8_t *alpha_palette = py_helper_keyword_alpha_palette(n_args, args, 8, kw_args, NULL);
|
||||
image_hint_t hint = py_helper_keyword_int(n_args, args, 9, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_hint), 0);
|
||||
|
||||
float arg_x_size;
|
||||
bool got_x_size = py_helper_keyword_float_maybe(n_args, args, 10, kw_args,
|
||||
MP_OBJ_NEW_QSTR(MP_QSTR_x_size), &arg_x_size);
|
||||
|
||||
float arg_y_size;
|
||||
bool got_y_size = py_helper_keyword_float_maybe(n_args, args, 11, kw_args,
|
||||
MP_OBJ_NEW_QSTR(MP_QSTR_y_size), &arg_y_size);
|
||||
|
||||
if (got_x_scale && got_x_size) {
|
||||
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Choose either x_scale or x_size not both!"));
|
||||
}
|
||||
if (got_y_scale && got_y_size) {
|
||||
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Choose either y_scale or y_size not both!"));
|
||||
}
|
||||
|
||||
if (got_x_size) {
|
||||
arg_x_scale = arg_x_size / arg_roi.w;
|
||||
}
|
||||
if (got_y_size) {
|
||||
arg_y_scale = arg_y_size / arg_roi.h;
|
||||
}
|
||||
|
||||
if ((!got_x_scale) && (!got_x_size) && got_y_size) {
|
||||
arg_x_scale = arg_y_scale;
|
||||
}
|
||||
if ((!got_y_scale) && (!got_y_size) && got_x_size) {
|
||||
arg_y_scale = arg_x_scale;
|
||||
}
|
||||
|
||||
if ((!self->triple_buffer) && (arg_y_scale < 0)) {
|
||||
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Vertical flip requires triple buffering!"));
|
||||
}
|
||||
|
||||
fb_alloc_mark();
|
||||
py_display_p_t *display_p = (py_display_p_t *) MP_OBJ_TYPE_GET_SLOT(self->base.type, protocol);
|
||||
display_p->write(self, arg_img, arg_x_off, arg_y_off, arg_x_scale, arg_y_scale, &arg_roi,
|
||||
arg_rgb_channel, arg_alpha, color_palette, alpha_palette, hint);
|
||||
fb_alloc_free_till_mark();
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_display_write_obj, 2, py_display_write);
|
||||
|
||||
STATIC const mp_rom_map_elem_t py_display_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_display) },
|
||||
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&py_display_deinit_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&py_display_width_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&py_display_height_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_triple_buffer), MP_ROM_PTR(&py_display_triple_buffer_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_bgr), MP_ROM_PTR(&py_display_bgr_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_byte_swap), MP_ROM_PTR(&py_display_byte_swap_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_framesize), MP_ROM_PTR(&py_display_framesize_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_refresh), MP_ROM_PTR(&py_display_refresh_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&py_display_clear_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_backlight), MP_ROM_PTR(&py_display_backlight_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&py_display_write_obj) },
|
||||
};
|
||||
MP_DEFINE_CONST_DICT(py_display_locals_dict, py_display_locals_dict_table);
|
||||
|
||||
STATIC const mp_rom_map_elem_t globals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_display) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_QVGA), MP_ROM_INT(DISPLAY_RESOLUTION_QVGA) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_TQVGA), MP_ROM_INT(DISPLAY_RESOLUTION_TQVGA) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_FHVGA), MP_ROM_INT(DISPLAY_RESOLUTION_FHVGA) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_FHVGA2), MP_ROM_INT(DISPLAY_RESOLUTION_FHVGA2) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_VGA), MP_ROM_INT(DISPLAY_RESOLUTION_VGA) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_THVGA), MP_ROM_INT(DISPLAY_RESOLUTION_THVGA) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_FWVGA), MP_ROM_INT(DISPLAY_RESOLUTION_FWVGA) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_FWVGA2), MP_ROM_INT(DISPLAY_RESOLUTION_FWVGA2) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_TFWVGA), MP_ROM_INT(DISPLAY_RESOLUTION_TFWVGA) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_TFWVGA2), MP_ROM_INT(DISPLAY_RESOLUTION_TFWVGA2) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_SVGA), MP_ROM_INT(DISPLAY_RESOLUTION_SVGA) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_WSVGA), MP_ROM_INT(DISPLAY_RESOLUTION_WSVGA) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_XGA), MP_ROM_INT(DISPLAY_RESOLUTION_XGA) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_SXGA), MP_ROM_INT(DISPLAY_RESOLUTION_SXGA) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_SXGA2), MP_ROM_INT(DISPLAY_RESOLUTION_SXGA2) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_UXGA), MP_ROM_INT(DISPLAY_RESOLUTION_UXGA) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_HD), MP_ROM_INT(DISPLAY_RESOLUTION_HD) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_FHD), MP_ROM_INT(DISPLAY_RESOLUTION_FHD) },
|
||||
|
||||
#ifdef OMV_SPI_DISPLAY_CONTROLLER
|
||||
{ MP_ROM_QSTR(MP_QSTR_SPIDisplay), MP_ROM_PTR(&py_spi_display_type) },
|
||||
#endif
|
||||
#ifdef OMV_RGB_DISPLAY_CONTROLLER
|
||||
{ MP_ROM_QSTR(MP_QSTR_RGBDisplay), MP_ROM_PTR(&py_rgb_display_type) },
|
||||
#endif
|
||||
#ifdef OMV_DSI_DISPLAY_CONTROLLER
|
||||
{ MP_ROM_QSTR(MP_QSTR_DSIDisplay), MP_ROM_PTR(&py_dsi_display_type) },
|
||||
#endif
|
||||
#if OMV_DISPLAY_CEC_ENABLE || OMV_DISPLAY_DDC_ENABLE
|
||||
{ MP_ROM_QSTR(MP_QSTR_DisplayData), MP_ROM_PTR(&py_display_data_type) },
|
||||
#endif
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(globals_dict, globals_dict_table);
|
||||
|
||||
const mp_obj_module_t display_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_t) &globals_dict,
|
||||
};
|
||||
|
||||
MP_REGISTER_MODULE(MP_QSTR_display, display_module);
|
||||
#endif // MICROPY_PY_DISPLAY
|
||||
78
src/omv/modules/py_display.h
Normal file
78
src/omv/modules/py_display.h
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* This file is part of the OpenMV project.
|
||||
*
|
||||
* Copyright (c) 2013-2023 Ibrahim Abdelkader <iabdalkader@openmv.io>
|
||||
* Copyright (c) 2013-2023 Kwabena W. Agyeman <kwagyeman@openmv.io>
|
||||
*
|
||||
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||
*
|
||||
* Display Python module.
|
||||
*/
|
||||
#ifndef __PY_DISPLAY_H__
|
||||
#define __PY_DISPLAY_H__
|
||||
|
||||
#include "omv_gpio.h"
|
||||
#include "omv_spi.h"
|
||||
#include "py_image.h"
|
||||
|
||||
#define FRAMEBUFFER_COUNT 3
|
||||
|
||||
typedef enum {
|
||||
DISPLAY_RESOLUTION_QVGA,
|
||||
DISPLAY_RESOLUTION_TQVGA,
|
||||
DISPLAY_RESOLUTION_FHVGA,
|
||||
DISPLAY_RESOLUTION_FHVGA2,
|
||||
DISPLAY_RESOLUTION_VGA,
|
||||
DISPLAY_RESOLUTION_THVGA,
|
||||
DISPLAY_RESOLUTION_FWVGA,
|
||||
DISPLAY_RESOLUTION_FWVGA2,
|
||||
DISPLAY_RESOLUTION_TFWVGA,
|
||||
DISPLAY_RESOLUTION_TFWVGA2,
|
||||
DISPLAY_RESOLUTION_SVGA,
|
||||
DISPLAY_RESOLUTION_WSVGA,
|
||||
DISPLAY_RESOLUTION_XGA,
|
||||
DISPLAY_RESOLUTION_SXGA,
|
||||
DISPLAY_RESOLUTION_SXGA2,
|
||||
DISPLAY_RESOLUTION_UXGA,
|
||||
DISPLAY_RESOLUTION_HD,
|
||||
DISPLAY_RESOLUTION_FHD,
|
||||
DISPLAY_RESOLUTION_MAX
|
||||
} display_resolution_t;
|
||||
|
||||
typedef struct _py_display_obj_t {
|
||||
mp_obj_base_t base;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t framesize;
|
||||
uint32_t refresh;
|
||||
uint32_t intensity;
|
||||
bool bgr;
|
||||
bool byte_swap;
|
||||
bool display_on;
|
||||
#if defined(OMV_SPI_DISPLAY_CONTROLLER)
|
||||
omv_spi_t spi_bus;
|
||||
bool spi_tx_running;
|
||||
uint32_t spi_baudrate;
|
||||
#endif
|
||||
bool triple_buffer;
|
||||
uint32_t framebuffer_tail;
|
||||
volatile uint32_t framebuffer_head;
|
||||
uint16_t *framebuffers[FRAMEBUFFER_COUNT];
|
||||
} py_display_obj_t;
|
||||
|
||||
// Display protocol
|
||||
typedef struct _py_display_p_t {
|
||||
void (*deinit) (py_display_obj_t *self);
|
||||
void (*clear) (py_display_obj_t *self, bool display_off);
|
||||
void (*write) (py_display_obj_t *self, image_t *src_img, int dst_x_start, int dst_y_start,
|
||||
float x_scale, float y_scale, rectangle_t *roi, int rgb_channel, int alpha,
|
||||
const uint16_t *color_palette, const uint8_t *alpha_palette, image_hint_t hint);
|
||||
void (*set_backlight) (py_display_obj_t *self, uint32_t intensity);
|
||||
} py_display_p_t;
|
||||
|
||||
extern const mp_obj_type_t py_spi_display_type;
|
||||
extern const mp_obj_type_t py_rgb_display_type;
|
||||
extern const mp_obj_type_t py_dsi_display_type;
|
||||
extern const mp_obj_type_t py_display_data_type;
|
||||
extern const mp_obj_dict_t py_display_locals_dict;
|
||||
#endif // __PY_DISPLAY_H__
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,14 +0,0 @@
|
||||
/*
|
||||
* This file is part of the OpenMV project.
|
||||
*
|
||||
* Copyright (c) 2013-2021 Ibrahim Abdelkader <iabdalkader@openmv.io>
|
||||
* Copyright (c) 2013-2021 Kwabena W. Agyeman <kwagyeman@openmv.io>
|
||||
*
|
||||
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||
*
|
||||
* LCD Python module.
|
||||
*/
|
||||
#ifndef __PY_LCD_H__
|
||||
#define __PY_LCD_H__
|
||||
void py_lcd_init0();
|
||||
#endif // __PY_LCD_H__
|
||||
454
src/omv/modules/py_spi_display.c
Normal file
454
src/omv/modules/py_spi_display.c
Normal file
@ -0,0 +1,454 @@
|
||||
/*
|
||||
* This file is part of the OpenMV project.
|
||||
*
|
||||
* Copyright (c) 2013-2021 Ibrahim Abdelkader <iabdalkader@openmv.io>
|
||||
* Copyright (c) 2013-2021 Kwabena W. Agyeman <kwagyeman@openmv.io>
|
||||
*
|
||||
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||
*
|
||||
* SPI Display Python module.
|
||||
*/
|
||||
#include "omv_boardconfig.h"
|
||||
|
||||
#if MICROPY_PY_DISPLAY && defined(OMV_SPI_DISPLAY_CONTROLLER)
|
||||
|
||||
#include "py/obj.h"
|
||||
#include "py/nlr.h"
|
||||
#include "py/runtime.h"
|
||||
#include "mphal.h"
|
||||
|
||||
#include "py_image.h"
|
||||
#include "omv_gpio.h"
|
||||
#include "omv_spi.h"
|
||||
#include "py_display.h"
|
||||
|
||||
#define LCD_COMMAND_DISPOFF (0x28)
|
||||
#define LCD_COMMAND_DISPON (0x29)
|
||||
#define LCD_COMMAND_RAMWR (0x2C)
|
||||
#define LCD_COMMAND_SLPOUT (0x11)
|
||||
#define LCD_COMMAND_MADCTL (0x36)
|
||||
#define LCD_COMMAND_COLMOD (0x3A)
|
||||
|
||||
#if OMV_SPI_DISPLAY_TRIPLE_BUFFER
|
||||
#define LCD_TRIPLE_BUFFER_DEFAULT (true)
|
||||
#else
|
||||
#define LCD_TRIPLE_BUFFER_DEFAULT (false)
|
||||
#endif
|
||||
|
||||
static void spi_transmit(py_display_obj_t *self, uint8_t *txdata, uint16_t size) {
|
||||
omv_spi_transfer_t spi_xfer = {
|
||||
.txbuf = txdata,
|
||||
.size = size,
|
||||
.timeout = OMV_SPI_MAX_TIMEOUT,
|
||||
.flags = OMV_SPI_XFER_BLOCKING
|
||||
};
|
||||
|
||||
omv_gpio_write(OMV_SPI_DISPLAY_SSEL_PIN, 0);
|
||||
omv_spi_transfer_start(&self->spi_bus, &spi_xfer);
|
||||
omv_gpio_write(OMV_SPI_DISPLAY_SSEL_PIN, 1);
|
||||
}
|
||||
|
||||
static void spi_transmit_16(py_display_obj_t *self, uint8_t *txdata, uint16_t size) {
|
||||
omv_spi_transfer_t spi_xfer = {
|
||||
.txbuf = txdata,
|
||||
.size = (!self->byte_swap) ? size : (size * 2),
|
||||
.timeout = OMV_SPI_MAX_TIMEOUT,
|
||||
.flags = OMV_SPI_XFER_BLOCKING,
|
||||
};
|
||||
|
||||
omv_spi_transfer_start(&self->spi_bus, &spi_xfer);
|
||||
}
|
||||
|
||||
static void spi_switch_mode(py_display_obj_t *self, int bits, bool dma) {
|
||||
omv_spi_deinit(&self->spi_bus);
|
||||
|
||||
omv_spi_config_t spi_config;
|
||||
omv_spi_default_config(&spi_config, OMV_SPI_DISPLAY_CONTROLLER);
|
||||
|
||||
spi_config.baudrate = self->spi_baudrate;
|
||||
spi_config.datasize = bits;
|
||||
spi_config.bus_mode = OMV_SPI_BUS_TX;
|
||||
spi_config.nss_enable = false;
|
||||
spi_config.dma_flags = dma ? OMV_SPI_DMA_NORMAL : 0;
|
||||
omv_spi_init(&self->spi_bus, &spi_config);
|
||||
}
|
||||
|
||||
static void spi_display_command(py_display_obj_t *self, uint8_t cmd, uint8_t arg) {
|
||||
omv_gpio_write(OMV_SPI_DISPLAY_RS_PIN, 0);
|
||||
spi_transmit(self, (uint8_t []) { cmd }, 1);
|
||||
omv_gpio_write(OMV_SPI_DISPLAY_RS_PIN, 1);
|
||||
if (arg) {
|
||||
spi_transmit(self, (uint8_t []) { arg }, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void spi_display_callback(omv_spi_t *spi, void *userdata, void *buf) {
|
||||
py_display_obj_t *self = (py_display_obj_t *) userdata;
|
||||
|
||||
static uint8_t *spi_state_write_addr = NULL;
|
||||
static size_t spi_state_write_count = 0;
|
||||
|
||||
// If userdata is not null then it means that we are being kicked off.
|
||||
if (buf == NULL) {
|
||||
spi_state_write_count = 0;
|
||||
}
|
||||
|
||||
if (!spi_state_write_count) {
|
||||
spi_state_write_addr = (uint8_t *) self->framebuffers[self->framebuffer_tail];
|
||||
spi_state_write_count = self->width * self->height;
|
||||
|
||||
if (self->byte_swap) {
|
||||
spi_state_write_count *= 2;
|
||||
}
|
||||
|
||||
self->framebuffer_head = self->framebuffer_tail;
|
||||
}
|
||||
|
||||
size_t spi_state_write_limit = (!self->byte_swap) ? OMV_SPI_MAX_16BIT_XFER : OMV_SPI_MAX_8BIT_XFER;
|
||||
uint8_t *addr = spi_state_write_addr;
|
||||
size_t count = IM_MIN(spi_state_write_count, spi_state_write_limit);
|
||||
|
||||
spi_state_write_addr += (!self->byte_swap) ? (count * 2) : count;
|
||||
spi_state_write_count -= count;
|
||||
|
||||
// When starting the interrupt chain the first transfer is not executed in interrupt context.
|
||||
// So, disable interrupts for the first transfer so that it completes first and unlocks the
|
||||
// SPI bus before allowing the interrupt it causes to trigger starting the interrupt chain.
|
||||
omv_spi_transfer_t spi_xfer = {
|
||||
.txbuf = addr,
|
||||
.size = count,
|
||||
.flags = OMV_SPI_XFER_DMA,
|
||||
.userdata = self,
|
||||
.callback = spi_display_callback,
|
||||
};
|
||||
|
||||
if (buf == NULL) {
|
||||
uint32_t irq_state = disable_irq();
|
||||
omv_spi_transfer_start(&self->spi_bus, &spi_xfer);
|
||||
enable_irq(irq_state);
|
||||
} else {
|
||||
omv_spi_transfer_start(&self->spi_bus, &spi_xfer);
|
||||
}
|
||||
}
|
||||
|
||||
static void spi_display_kick(py_display_obj_t *self) {
|
||||
if (!self->spi_tx_running) {
|
||||
spi_display_command(self, LCD_COMMAND_RAMWR, 0);
|
||||
spi_switch_mode(self, (!self->byte_swap) ? 16 : 8, true);
|
||||
omv_gpio_write(OMV_SPI_DISPLAY_SSEL_PIN, 0);
|
||||
|
||||
// Limit the transfer size to single lines as you cannot send more
|
||||
// than 64KB per SPI transaction generally.
|
||||
for (int i = 0; i < self->height; i++) {
|
||||
uint8_t *buffer = (uint8_t *) (self->framebuffers[self->framebuffer_tail] + (self->width * i));
|
||||
spi_transmit_16(self, buffer, self->width);
|
||||
}
|
||||
|
||||
spi_switch_mode(self, 8, false);
|
||||
omv_gpio_write(OMV_SPI_DISPLAY_SSEL_PIN, 1);
|
||||
spi_display_command(self, LCD_COMMAND_DISPON, 0);
|
||||
spi_display_command(self, LCD_COMMAND_RAMWR, 0);
|
||||
spi_switch_mode(self, (!self->byte_swap) ? 16 : 8, true);
|
||||
omv_gpio_write(OMV_SPI_DISPLAY_SSEL_PIN, 0);
|
||||
|
||||
// Kickoff interrupt driven image update.
|
||||
self->spi_tx_running = true;
|
||||
spi_display_callback(&self->spi_bus, self, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO remove this when imlib_draw_image accepts a context.
|
||||
static py_display_obj_t *lcd_self;
|
||||
|
||||
static void spi_display_draw_image_cb(int x_start, int x_end, int y_row, imlib_draw_row_data_t *data) {
|
||||
spi_transmit_16(lcd_self, data->dst_row_override, lcd_self->width);
|
||||
}
|
||||
|
||||
static void spi_display_write(py_display_obj_t *self, image_t *src_img, int dst_x_start, int dst_y_start,
|
||||
float x_scale, float y_scale, rectangle_t *roi, int rgb_channel, int alpha,
|
||||
const uint16_t *color_palette, const uint8_t *alpha_palette, image_hint_t hint) {
|
||||
image_t dst_img;
|
||||
dst_img.w = self->width;
|
||||
dst_img.h = self->height;
|
||||
dst_img.pixfmt = PIXFORMAT_RGB565;
|
||||
|
||||
int x0, x1, y0, y1;
|
||||
bool black = !imlib_draw_image_rectangle(&dst_img, src_img, dst_x_start, dst_y_start, x_scale,
|
||||
y_scale, roi, alpha, alpha_palette, hint, &x0, &x1, &y0, &y1);
|
||||
|
||||
if (!self->triple_buffer) {
|
||||
dst_img.data = fb_alloc0(self->width * sizeof(uint16_t), FB_ALLOC_NO_HINT);
|
||||
|
||||
spi_display_command(self, LCD_COMMAND_RAMWR, 0);
|
||||
spi_switch_mode(self, (!self->byte_swap) ? 16 : 8, true);
|
||||
omv_gpio_write(OMV_SPI_DISPLAY_SSEL_PIN, 0);
|
||||
|
||||
if (black) {
|
||||
// zero the whole image
|
||||
for (int i = 0; i < self->height; i++) {
|
||||
spi_transmit_16(self, dst_img.data, self->width);
|
||||
}
|
||||
} else {
|
||||
// Zero the top rows
|
||||
for (int i = 0; i < y0; i++) {
|
||||
spi_transmit_16(self, dst_img.data, self->width);
|
||||
}
|
||||
|
||||
// TODO remove this when imlib_draw_image accepts a context.
|
||||
lcd_self = self;
|
||||
|
||||
// Transmits left/right parts already zeroed...
|
||||
imlib_draw_image(&dst_img, src_img, dst_x_start, dst_y_start,
|
||||
x_scale, y_scale, roi, rgb_channel, alpha, color_palette, alpha_palette,
|
||||
hint | IMAGE_HINT_BLACK_BACKGROUND, spi_display_draw_image_cb, dst_img.data);
|
||||
// Zero the bottom rows
|
||||
if (y1 < self->height) {
|
||||
memset(dst_img.data, 0, self->width * sizeof(uint16_t));
|
||||
}
|
||||
|
||||
for (int i = y1; i < self->height; i++) {
|
||||
spi_transmit_16(self, dst_img.data, self->width);
|
||||
}
|
||||
}
|
||||
|
||||
spi_switch_mode(self, 8, false);
|
||||
omv_gpio_write(OMV_SPI_DISPLAY_SSEL_PIN, 1);
|
||||
spi_display_command(self, LCD_COMMAND_DISPON, 0);
|
||||
fb_free();
|
||||
} else {
|
||||
// For triple buffering we are never drawing where tail or head
|
||||
// (which may instantly update to to be equal to tail) is.
|
||||
int new_framebuffer_tail = (self->framebuffer_tail + 1) % FRAMEBUFFER_COUNT;
|
||||
if (new_framebuffer_tail == self->framebuffer_head) {
|
||||
new_framebuffer_tail = (new_framebuffer_tail + 1) % FRAMEBUFFER_COUNT;
|
||||
}
|
||||
dst_img.data = (uint8_t *) self->framebuffers[new_framebuffer_tail];
|
||||
|
||||
if (black) {
|
||||
// zero the whole image
|
||||
memset(dst_img.data, 0, self->width * self->height * sizeof(uint16_t));
|
||||
} else {
|
||||
// Zero the top rows
|
||||
if (y0) {
|
||||
memset(dst_img.data, 0, self->width * y0 * sizeof(uint16_t));
|
||||
}
|
||||
|
||||
if (x0) {
|
||||
for (int i = y0; i < y1; i++) {
|
||||
// Zero left
|
||||
memset(IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&dst_img, i), 0, x0 * sizeof(uint16_t));
|
||||
}
|
||||
}
|
||||
|
||||
imlib_draw_image(&dst_img, src_img, dst_x_start, dst_y_start,
|
||||
x_scale, y_scale, roi, rgb_channel, alpha, color_palette,
|
||||
alpha_palette, hint | IMAGE_HINT_BLACK_BACKGROUND, NULL, NULL);
|
||||
|
||||
if (self->width - x1) {
|
||||
for (int i = y0; i < y1; i++) {
|
||||
// Zero right
|
||||
memset(IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&dst_img, i) + x1, 0, (self->width - x1) * sizeof(uint16_t));
|
||||
}
|
||||
}
|
||||
|
||||
// Zero the bottom rows
|
||||
if (self->height - y1) {
|
||||
memset(IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&dst_img, y1),
|
||||
0, self->width * (self->height - y1) * sizeof(uint16_t));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __DCACHE_PRESENT
|
||||
// Flush data for DMA
|
||||
SCB_CleanDCache_by_Addr((uint32_t *) dst_img.data, image_size(&dst_img));
|
||||
#endif
|
||||
|
||||
// Update tail which means a new image is ready.
|
||||
self->framebuffer_tail = new_framebuffer_tail;
|
||||
|
||||
// Kick off an update of the display.
|
||||
spi_display_kick(self);
|
||||
}
|
||||
}
|
||||
|
||||
static void spi_display_clear(py_display_obj_t *self, bool display_off) {
|
||||
if (display_off) {
|
||||
// turns the display off (may not be black)
|
||||
if (self->spi_tx_running) {
|
||||
omv_spi_transfer_abort(&self->spi_bus);
|
||||
self->spi_tx_running = false;
|
||||
spi_switch_mode(self, 8, false);
|
||||
omv_gpio_write(OMV_SPI_DISPLAY_SSEL_PIN, 1);
|
||||
}
|
||||
} else {
|
||||
spi_display_command(self, LCD_COMMAND_DISPOFF, 0);
|
||||
fb_alloc_mark();
|
||||
spi_display_write(self, NULL, 0, 0, 1.f, 1.f, NULL, 0, 0, NULL, NULL, 0);
|
||||
fb_alloc_free_till_mark();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef OMV_DISPLAY_BL_DAC
|
||||
static DAC_HandleTypeDef lcd_dac_handle;
|
||||
#endif
|
||||
|
||||
#ifdef OMV_SPI_DISPLAY_BL_PIN
|
||||
static void spi_display_set_backlight(py_display_obj_t *self, uint32_t intensity) {
|
||||
#ifdef OMV_DISPLAY_BL_DAC
|
||||
if ((self->intensity < 255) && (255 <= intensity)) {
|
||||
#else
|
||||
if ((self->intensity < 1) && (1 <= intensity)) {
|
||||
#endif
|
||||
omv_gpio_write(OMV_SPI_DISPLAY_BL_PIN, 1);
|
||||
omv_gpio_deinit(OMV_SPI_DISPLAY_BL_PIN);
|
||||
} else if ((0 < self->intensity) && (intensity <= 0)) {
|
||||
omv_gpio_config(OMV_SPI_DISPLAY_BL_PIN, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1);
|
||||
omv_gpio_write(OMV_SPI_DISPLAY_BL_PIN, 0);
|
||||
}
|
||||
|
||||
#ifdef OMV_DISPLAY_BL_DAC
|
||||
if (((self->intensity <= 0) || (255 <= self->intensity)) && (0 < intensity) && (intensity < 255)) {
|
||||
omv_gpio_config(OMV_SPI_DISPLAY_BL_PIN, OMV_GPIO_MODE_ANALOG, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1);
|
||||
|
||||
DAC_ChannelConfTypeDef lcd_dac_channel_handle;
|
||||
lcd_dac_handle.Instance = OMV_DISPLAY_BL_DAC;
|
||||
lcd_dac_channel_handle.DAC_Trigger = DAC_TRIGGER_NONE;
|
||||
lcd_dac_channel_handle.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
|
||||
#if defined(MCU_SERIES_H7)
|
||||
lcd_dac_channel_handle.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;
|
||||
lcd_dac_channel_handle.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_DISABLE;
|
||||
lcd_dac_channel_handle.DAC_UserTrimming = DAC_TRIMMING_FACTORY;
|
||||
#endif
|
||||
|
||||
HAL_DAC_Init(&lcd_dac_handle);
|
||||
HAL_DAC_ConfigChannel(&lcd_dac_handle, &lcd_dac_channel_handle, OMV_DISPLAY_BL_DAC_CHANNEL);
|
||||
HAL_DAC_Start(&lcd_dac_handle, OMV_DISPLAY_BL_DAC_CHANNEL);
|
||||
HAL_DAC_SetValue(&lcd_dac_handle, OMV_DISPLAY_BL_DAC_CHANNEL, DAC_ALIGN_8B_R, intensity);
|
||||
} else if ((0 < self->intensity) && (self->intensity < 255) && ((intensity <= 0) || (255 <= intensity))) {
|
||||
HAL_DAC_Stop(&lcd_dac_handle, OMV_DISPLAY_BL_DAC_CHANNEL);
|
||||
HAL_DAC_DeInit(&lcd_dac_handle);
|
||||
} else if ((0 < self->intensity) && (self->intensity < 255) && (0 < intensity) && (intensity < 255)) {
|
||||
HAL_DAC_SetValue(&lcd_dac_handle, OMV_DISPLAY_BL_DAC_CHANNEL, DAC_ALIGN_8B_R, intensity);
|
||||
}
|
||||
#endif
|
||||
|
||||
self->intensity = intensity;
|
||||
}
|
||||
#endif // OMV_SPI_DISPLAY_BL_PIN
|
||||
|
||||
static void spi_display_deinit(py_display_obj_t *self) {
|
||||
if (self->triple_buffer) {
|
||||
omv_spi_transfer_abort(&self->spi_bus);
|
||||
fb_alloc_free_till_mark_past_mark_permanent();
|
||||
}
|
||||
|
||||
omv_spi_deinit(&self->spi_bus);
|
||||
omv_gpio_deinit(OMV_SPI_DISPLAY_RS_PIN);
|
||||
omv_gpio_deinit(OMV_SPI_DISPLAY_RST_PIN);
|
||||
|
||||
#ifdef OMV_SPI_DISPLAY_BL_PIN
|
||||
spi_display_set_backlight(self, 255);
|
||||
#endif
|
||||
}
|
||||
|
||||
mp_obj_t spi_display_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
||||
enum { ARG_width, ARG_height, ARG_refresh, ARG_triple_buffer, ARG_bgr, ARG_byte_swap};
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_width, MP_ARG_INT, {.u_int = 128 } },
|
||||
{ MP_QSTR_height, MP_ARG_INT, {.u_int = 160 } },
|
||||
{ MP_QSTR_refresh, MP_ARG_INT, {.u_int = 60 } },
|
||||
{ MP_QSTR_bgr, MP_ARG_BOOL, {.u_bool = false} },
|
||||
{ MP_QSTR_byte_swap, MP_ARG_BOOL, {.u_bool = false} },
|
||||
{ MP_QSTR_triple_buffer, MP_ARG_BOOL, {.u_bool = LCD_TRIPLE_BUFFER_DEFAULT} },
|
||||
};
|
||||
|
||||
// Parse args.
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
if ((args[ARG_width].u_int <= 0) || (args[ARG_width].u_int > 32767)) {
|
||||
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Invalid Width!"));
|
||||
}
|
||||
if ((args[ARG_height].u_int <= 0) || (args[ARG_height].u_int > 32767)) {
|
||||
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Invalid Height!"));
|
||||
}
|
||||
if ((args[ARG_refresh].u_int < 30) || (args[ARG_refresh].u_int > 120)) {
|
||||
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Invalid Refresh Rate!"));
|
||||
}
|
||||
|
||||
py_display_obj_t *self = m_new_obj_with_finaliser(py_display_obj_t);
|
||||
self->base.type = &py_spi_display_type;
|
||||
self->framebuffer_tail = 0;
|
||||
self->framebuffer_head = 0;
|
||||
self->width = args[ARG_width].u_int;
|
||||
self->height = args[ARG_height].u_int;
|
||||
self->refresh = args[ARG_refresh].u_int;
|
||||
self->triple_buffer = args[ARG_triple_buffer].u_bool;
|
||||
self->bgr = args[ARG_bgr].u_bool;
|
||||
self->byte_swap = args[ARG_byte_swap].u_bool;
|
||||
|
||||
omv_spi_config_t spi_config;
|
||||
omv_spi_default_config(&spi_config, OMV_SPI_DISPLAY_CONTROLLER);
|
||||
|
||||
self->spi_baudrate = self->width * self->height * self->refresh * 16;
|
||||
spi_config.baudrate = self->spi_baudrate;
|
||||
spi_config.bus_mode = OMV_SPI_BUS_TX;
|
||||
spi_config.nss_enable = false;
|
||||
omv_spi_init(&self->spi_bus, &spi_config);
|
||||
omv_gpio_write(OMV_SPI_DISPLAY_SSEL_PIN, 1);
|
||||
|
||||
omv_gpio_config(OMV_SPI_DISPLAY_RST_PIN, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1);
|
||||
omv_gpio_write(OMV_SPI_DISPLAY_RST_PIN, 1);
|
||||
|
||||
omv_gpio_config(OMV_SPI_DISPLAY_RS_PIN, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1);
|
||||
omv_gpio_write(OMV_SPI_DISPLAY_RS_PIN, 1);
|
||||
|
||||
// Reset LCD
|
||||
omv_gpio_write(OMV_SPI_DISPLAY_RST_PIN, 0);
|
||||
mp_hal_delay_ms(100);
|
||||
omv_gpio_write(OMV_SPI_DISPLAY_RST_PIN, 1);
|
||||
mp_hal_delay_ms(100);
|
||||
|
||||
// Sleep out
|
||||
spi_display_command(self, LCD_COMMAND_SLPOUT, 0);
|
||||
mp_hal_delay_ms(120);
|
||||
// Memory data access control
|
||||
spi_display_command(self, LCD_COMMAND_MADCTL, self->bgr ? 0xC8 : 0xC0);
|
||||
// Interface pixel format
|
||||
spi_display_command(self, LCD_COMMAND_COLMOD, 0x05);
|
||||
|
||||
if (self->triple_buffer) {
|
||||
fb_alloc_mark();
|
||||
uint32_t fb_size = self->width * self->height * sizeof(uint16_t);
|
||||
for (int i = 0; i < FRAMEBUFFER_COUNT; i++) {
|
||||
self->framebuffers[i] = (uint16_t *) fb_alloc0(fb_size, FB_ALLOC_CACHE_ALIGN);
|
||||
}
|
||||
fb_alloc_mark_permanent();
|
||||
}
|
||||
|
||||
#ifdef OMV_SPI_DISPLAY_BL_PIN
|
||||
spi_display_set_backlight(self, 255);
|
||||
#endif
|
||||
|
||||
return MP_OBJ_FROM_PTR(self);
|
||||
}
|
||||
|
||||
STATIC const py_display_p_t py_display_p = {
|
||||
.deinit = spi_display_deinit,
|
||||
.clear = spi_display_clear,
|
||||
.write = spi_display_write,
|
||||
#ifdef OMV_SPI_DISPLAY_BL_PIN
|
||||
.set_backlight = spi_display_set_backlight,
|
||||
#endif
|
||||
};
|
||||
|
||||
MP_DEFINE_CONST_OBJ_TYPE(
|
||||
py_spi_display_type,
|
||||
MP_QSTR_SPIDisplay,
|
||||
MP_TYPE_FLAG_NONE,
|
||||
make_new, spi_display_make_new,
|
||||
protocol, &py_display_p,
|
||||
locals_dict, &py_display_locals_dict
|
||||
);
|
||||
|
||||
#endif // MICROPY_PY_DISPLAY
|
||||
@ -34,7 +34,7 @@
|
||||
#error "TV_HEIGHT not even"
|
||||
#endif
|
||||
|
||||
#ifdef OMV_SPI_LCD_SPI_BUS
|
||||
#ifdef OMV_SPI_DISPLAY_CONTROLLER
|
||||
/////////////////////////////////////////////////////////////
|
||||
// http://www.vsdsp-forum.com/phpbb/viewtopic.php?f=14&t=1801
|
||||
/////////////////////////////////////////////////////////////
|
||||
@ -219,11 +219,11 @@ static void SpiTransmitReceivePacket(uint8_t *txdata, uint8_t *rxdata, uint16_t
|
||||
.flags = OMV_SPI_XFER_BLOCKING
|
||||
};
|
||||
|
||||
omv_gpio_write(OMV_SPI_LCD_SSEL_PIN, 0);
|
||||
omv_gpio_write(OMV_SPI_DISPLAY_SSEL_PIN, 0);
|
||||
omv_spi_transfer_start(&spi_bus, &spi_xfer);
|
||||
|
||||
if (end) {
|
||||
omv_gpio_write(OMV_SPI_LCD_SSEL_PIN, 1);
|
||||
omv_gpio_write(OMV_SPI_DISPLAY_SSEL_PIN, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -395,7 +395,7 @@ tv_type = TV_NONE;
|
||||
|
||||
static bool tv_triple_buffer = false;
|
||||
|
||||
#ifdef OMV_SPI_LCD_SPI_BUS
|
||||
#ifdef OMV_SPI_DISPLAY_CONTROLLER
|
||||
static volatile enum {
|
||||
SPI_TX_CB_IDLE,
|
||||
SPI_TX_CB_MEMORY_WRITE_CMD,
|
||||
@ -415,14 +415,14 @@ static void spi_config_deinit() {
|
||||
|
||||
static void spi_config_init(bool triple_buffer) {
|
||||
omv_spi_config_t spi_config;
|
||||
omv_spi_default_config(&spi_config, OMV_SPI_LCD_SPI_BUS);
|
||||
omv_spi_default_config(&spi_config, OMV_SPI_DISPLAY_CONTROLLER);
|
||||
|
||||
spi_config.baudrate = TV_BAUDRATE;
|
||||
spi_config.nss_enable = false;
|
||||
spi_config.dma_flags = triple_buffer ? OMV_SPI_DMA_NORMAL : 0;
|
||||
omv_spi_init(&spi_bus, &spi_config);
|
||||
|
||||
omv_gpio_write(OMV_SPI_LCD_SSEL_PIN, 1);
|
||||
omv_gpio_write(OMV_SPI_DISPLAY_SSEL_PIN, 1);
|
||||
|
||||
SpiRamVideoInit();
|
||||
|
||||
@ -458,12 +458,12 @@ static void spi_tv_callback(omv_spi_t *spi, void *userdata, void *buf) {
|
||||
|
||||
switch (spi_tx_cb_state) {
|
||||
case SPI_TX_CB_MEMORY_WRITE_CMD: {
|
||||
omv_gpio_write(OMV_SPI_LCD_SSEL_PIN, 1);
|
||||
omv_gpio_write(OMV_SPI_DISPLAY_SSEL_PIN, 1);
|
||||
spi_tx_cb_state = SPI_TX_CB_MEMORY_WRITE;
|
||||
spi_tx_cb_state_memory_write_addr = (uint8_t *) framebuffers[framebuffer_head];
|
||||
spi_tx_cb_state_memory_write_count = PICLINE_LENGTH_BYTES * TV_HEIGHT;
|
||||
framebuffer_tail = framebuffer_head;
|
||||
omv_gpio_write(OMV_SPI_LCD_SSEL_PIN, 0);
|
||||
omv_gpio_write(OMV_SPI_DISPLAY_SSEL_PIN, 0);
|
||||
// When starting the interrupt chain the first transfer is not executed
|
||||
// in interrupt context. So, disable interrupts for the first transfer so
|
||||
// that it completes first and unlocks the SPI bus before allowing the interrupt
|
||||
@ -621,7 +621,7 @@ static void spi_tv_display(image_t *src_img, int dst_x_start, int dst_y_start, f
|
||||
}
|
||||
}
|
||||
|
||||
omv_gpio_write(OMV_SPI_LCD_SSEL_PIN, 1);
|
||||
omv_gpio_write(OMV_SPI_DISPLAY_SSEL_PIN, 1);
|
||||
fb_free();
|
||||
} else {
|
||||
// For triple buffering we are never drawing where head or tail (which may instantly update to
|
||||
@ -736,7 +736,7 @@ static void spi_tv_display(image_t *src_img, int dst_x_start, int dst_y_start, f
|
||||
|
||||
STATIC mp_obj_t py_tv_deinit() {
|
||||
switch (tv_type) {
|
||||
#ifdef OMV_SPI_LCD_SPI_BUS
|
||||
#ifdef OMV_SPI_DISPLAY_CONTROLLER
|
||||
case TV_SHIELD: {
|
||||
spi_config_deinit();
|
||||
break;
|
||||
@ -760,11 +760,11 @@ STATIC mp_obj_t py_tv_init(uint n_args, const mp_obj_t *args, mp_map_t *kw_args)
|
||||
MP_OBJ_NEW_QSTR(MP_QSTR_type), TV_SHIELD);
|
||||
|
||||
switch (type) {
|
||||
#ifdef OMV_SPI_LCD_SPI_BUS
|
||||
#ifdef OMV_SPI_DISPLAY_CONTROLLER
|
||||
case TV_SHIELD: {
|
||||
bool triple_buffer_def = false;
|
||||
#ifdef OMV_SPI_LCD_DEF_TRIPLE_BUF
|
||||
triple_buffer_def = OMV_SPI_LCD_DEF_TRIPLE_BUF;
|
||||
#ifdef OMV_SPI_DISPLAY_TRIPLE_BUFFER
|
||||
triple_buffer_def = OMV_SPI_DISPLAY_TRIPLE_BUFFER;
|
||||
#endif
|
||||
bool triple_buffer = py_helper_keyword_int(n_args, args, 1, kw_args,
|
||||
MP_OBJ_NEW_QSTR(MP_QSTR_triple_buffer), triple_buffer_def);
|
||||
@ -833,11 +833,11 @@ STATIC mp_obj_t py_tv_channel(uint n_args, const mp_obj_t *args) {
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
#ifdef OMV_SPI_LCD_SPI_BUS
|
||||
#ifdef OMV_SPI_DISPLAY_CONTROLLER
|
||||
if (tv_triple_buffer) {
|
||||
omv_spi_transfer_abort(&spi_bus);
|
||||
spi_tx_cb_state = SPI_TX_CB_IDLE;
|
||||
omv_gpio_write(OMV_SPI_LCD_SSEL_PIN, 1);
|
||||
omv_gpio_write(OMV_SPI_DISPLAY_SSEL_PIN, 1);
|
||||
}
|
||||
|
||||
if (n_args) {
|
||||
@ -849,11 +849,11 @@ STATIC mp_obj_t py_tv_channel(uint n_args, const mp_obj_t *args) {
|
||||
|
||||
SpiRamWriteByteRegister(WRITE_GPIO, 0x70 | (channel - 1));
|
||||
} else {
|
||||
#ifdef OMV_SPI_LCD_RX_CLK_DIV
|
||||
omv_spi_set_baudrate(&spi_bus, TV_BAUDRATE / OMV_SPI_LCD_RX_CLK_DIV);
|
||||
#ifdef OMV_SPI_DISPLAY_RX_CLK_DIV
|
||||
omv_spi_set_baudrate(&spi_bus, TV_BAUDRATE / OMV_SPI_DISPLAY_RX_CLK_DIV);
|
||||
#endif
|
||||
int channel = SpiRamReadByteRegister(READ_GPIO);
|
||||
#ifdef OMV_SPI_LCD_RX_CLK_DIV
|
||||
#ifdef OMV_SPI_DISPLAY_RX_CLK_DIV
|
||||
omv_spi_set_baudrate(&spi_bus, TV_BAUDRATE);
|
||||
#endif
|
||||
return mp_obj_new_int((channel & 0x7) + 1);
|
||||
@ -949,7 +949,7 @@ STATIC mp_obj_t py_tv_display(uint n_args, const mp_obj_t *args, mp_map_t *kw_ar
|
||||
}
|
||||
|
||||
switch (tv_type) {
|
||||
#ifdef OMV_SPI_LCD_SPI_BUS
|
||||
#ifdef OMV_SPI_DISPLAY_CONTROLLER
|
||||
case TV_SHIELD: {
|
||||
fb_alloc_mark();
|
||||
spi_tv_display(arg_img, arg_x_off, arg_y_off, arg_x_scale, arg_y_scale, &arg_roi,
|
||||
@ -969,7 +969,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_tv_display_obj, 1, py_tv_display);
|
||||
|
||||
STATIC mp_obj_t py_tv_clear() {
|
||||
switch (tv_type) {
|
||||
#ifdef OMV_SPI_LCD_SPI_BUS
|
||||
#ifdef OMV_SPI_DISPLAY_CONTROLLER
|
||||
case TV_SHIELD: {
|
||||
fb_alloc_mark();
|
||||
spi_tv_display(NULL, 0, 0, 1.f, 1.f, NULL,
|
||||
|
||||
@ -28,7 +28,6 @@
|
||||
#include "systick.h"
|
||||
#include "modmimxrt.h"
|
||||
|
||||
#include "py_lcd.h"
|
||||
#include "py_fir.h"
|
||||
#include "py_tv.h"
|
||||
|
||||
@ -115,9 +114,6 @@ soft_reset:
|
||||
mp_init();
|
||||
|
||||
// Initialise low-level sub-systems.
|
||||
#if MICROPY_PY_LCD
|
||||
py_lcd_init0();
|
||||
#endif
|
||||
py_fir_init0();
|
||||
#if MICROPY_PY_TV
|
||||
py_tv_init0();
|
||||
|
||||
@ -67,7 +67,6 @@
|
||||
#include "usbd_cdc_interface.h"
|
||||
|
||||
#include "py_image.h"
|
||||
#include "py_lcd.h"
|
||||
#include "py_fir.h"
|
||||
#include "py_tv.h"
|
||||
#include "py_buzzer.h"
|
||||
@ -359,9 +358,6 @@ soft_reset:
|
||||
|
||||
// Initialise low-level sub-systems. Here we need to do the very basic
|
||||
// things like zeroing out memory and resetting any of the sub-systems.
|
||||
#if MICROPY_PY_LCD
|
||||
py_lcd_init0();
|
||||
#endif
|
||||
py_fir_init0();
|
||||
#if MICROPY_PY_TV
|
||||
py_tv_init0();
|
||||
|
||||
510
src/omv/ports/stm32/modules/py_display.c
Normal file
510
src/omv/ports/stm32/modules/py_display.c
Normal file
@ -0,0 +1,510 @@
|
||||
/*
|
||||
* This file is part of the OpenMV project.
|
||||
*
|
||||
* Copyright (c) 2013-2021 Ibrahim Abdelkader <iabdalkader@openmv.io>
|
||||
* Copyright (c) 2013-2021 Kwabena W. Agyeman <kwagyeman@openmv.io>
|
||||
*
|
||||
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||
*
|
||||
* LTDC display Python module.
|
||||
*/
|
||||
#include "omv_boardconfig.h"
|
||||
|
||||
#if MICROPY_PY_DISPLAY && defined(OMV_RGB_DISPLAY_CONTROLLER)
|
||||
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "mphal.h"
|
||||
|
||||
#include "py_helper.h"
|
||||
#include "py_image.h"
|
||||
#include "omv_gpio.h"
|
||||
#include "py_display.h"
|
||||
|
||||
#if defined(OMV_RGB_DISPLAY_BL_PIN)
|
||||
#define OMV_DISPLAY_BL_PIN OMV_RGB_DISPLAY_BL_PIN
|
||||
#endif
|
||||
|
||||
#if defined(OMV_RGB_DISPLAY_DISP_PIN)
|
||||
#define OMV_DISPLAY_DISP_PIN OMV_RGB_DISPLAY_DISP_PIN
|
||||
#endif
|
||||
|
||||
typedef struct display_mode {
|
||||
uint32_t hactive;
|
||||
uint32_t vactive;
|
||||
uint32_t pixel_clock;
|
||||
uint32_t hsync_len;
|
||||
uint32_t hback_porch;
|
||||
uint32_t hfront_porch;
|
||||
uint32_t vsync_len;
|
||||
uint32_t vback_porch;
|
||||
uint32_t vfront_porch;
|
||||
uint32_t hpol : 1;
|
||||
uint32_t vpol : 1;
|
||||
} display_mode_t;
|
||||
|
||||
typedef struct _display_state {
|
||||
py_display_obj_t *self;
|
||||
LTDC_HandleTypeDef hltdc;
|
||||
#ifdef OMV_DISPLAY_BL_TIM
|
||||
TIM_HandleTypeDef htim;
|
||||
#endif
|
||||
LTDC_LayerCfgTypeDef framebuffer_layers[FRAMEBUFFER_COUNT];
|
||||
} display_state_t;
|
||||
|
||||
static display_state_t display;
|
||||
|
||||
static const display_mode_t display_modes[] = {
|
||||
{ // QVGA
|
||||
.hactive=320, .vactive=240, .pixel_clock=6144,
|
||||
.hsync_len=32, .hback_porch=40, .hfront_porch=8,
|
||||
.vsync_len=8, .vback_porch=6, .vfront_porch=1
|
||||
},
|
||||
{ // TQVGA
|
||||
.hactive=240, .vactive=320, .pixel_clock=6426,
|
||||
.hsync_len=32, .hback_porch=40, .hfront_porch=8,
|
||||
.vsync_len=8, .vback_porch=6, .vfront_porch=1
|
||||
},
|
||||
{ // FHVGA
|
||||
.hactive=480, .vactive=272, .pixel_clock=9633,
|
||||
.hsync_len=32, .hback_porch=40, .hfront_porch=8,
|
||||
.vsync_len=8, .vback_porch=6, .vfront_porch=1
|
||||
},
|
||||
{ // FHVGA2
|
||||
.hactive=480, .vactive=128, .pixel_clock=4799,
|
||||
.hsync_len=32, .hback_porch=40, .hfront_porch=8,
|
||||
.vsync_len=8, .vback_porch=6, .vfront_porch=1
|
||||
},
|
||||
{ // VGA
|
||||
.hactive=640, .vactive=480, .pixel_clock=21363,
|
||||
.hsync_len=32, .hback_porch=40, .hfront_porch=8,
|
||||
.vsync_len=8, .vback_porch=6, .vfront_porch=1
|
||||
},
|
||||
{ // THVGA
|
||||
.hactive=320, .vactive=480, .pixel_clock=11868,
|
||||
.hsync_len=32, .hback_porch=40, .hfront_porch=8,
|
||||
.vsync_len=8, .vback_porch=6, .vfront_porch=1
|
||||
},
|
||||
{ // FWVGA
|
||||
.hactive=800, .vactive=480, .pixel_clock=26110,
|
||||
.hsync_len=32, .hback_porch=40, .hfront_porch=8,
|
||||
.vsync_len=8, .vback_porch=6, .vfront_porch=1
|
||||
},
|
||||
{ // FWVGA2
|
||||
.hactive=800, .vactive=320, .pixel_clock=17670,
|
||||
.hsync_len=32, .hback_porch=40, .hfront_porch=8,
|
||||
.vsync_len=8, .vback_porch=6, .vfront_porch=1
|
||||
},
|
||||
{ // TFWVGA
|
||||
.hactive=480, .vactive=800, .pixel_clock=27624,
|
||||
.hsync_len=32, .hback_porch=40, .hfront_porch=8,
|
||||
.vsync_len=8, .vback_porch=6, .vfront_porch=9
|
||||
},
|
||||
{ // TFWVGA2
|
||||
.hactive=480, .vactive=480, .pixel_clock=16615,
|
||||
.hsync_len=32, .hback_porch=40, .hfront_porch=8,
|
||||
.vsync_len=8, .vback_porch=6, .vfront_porch=1
|
||||
},
|
||||
{ // SVGA
|
||||
.hactive=800, .vactive=600, .pixel_clock=32597,
|
||||
.hsync_len=32, .hback_porch=40, .hfront_porch=8,
|
||||
.vsync_len=8, .vback_porch=6, .vfront_porch=4
|
||||
},
|
||||
{ // WSVGA
|
||||
.hactive=1024, .vactive=600, .pixel_clock=40895,
|
||||
.hsync_len=32, .hback_porch=40, .hfront_porch=8,
|
||||
.vsync_len=8, .vback_porch=6, .vfront_porch=4
|
||||
},
|
||||
{ // XGA
|
||||
.hactive=1024, .vactive=768, .pixel_clock=52277,
|
||||
.hsync_len=32, .hback_porch=40, .hfront_porch=8,
|
||||
.vsync_len=8, .vback_porch=6, .vfront_porch=8
|
||||
},
|
||||
{ // SXGA
|
||||
.hactive=1280, .vactive=1024, .pixel_clock=85920,
|
||||
.hsync_len=32, .hback_porch=40, .hfront_porch=8,
|
||||
.vsync_len=8, .vback_porch=6, .vfront_porch=16
|
||||
},
|
||||
{ // SXGA2
|
||||
.hactive=1280, .vactive=400, .pixel_clock=33830,
|
||||
.hsync_len=32, .hback_porch=40, .hfront_porch=8,
|
||||
.vsync_len=8, .vback_porch=6, .vfront_porch=1
|
||||
},
|
||||
{ // UXGA
|
||||
.hactive=1600, .vactive=1200, .pixel_clock=124364,
|
||||
.hsync_len=32, .hback_porch=40, .hfront_porch=8,
|
||||
.vsync_len=8, .vback_porch=6, .vfront_porch=21
|
||||
},
|
||||
{ // HD
|
||||
.hactive=1280, .vactive=720, .pixel_clock=60405,
|
||||
.hsync_len=32, .hback_porch=40, .hfront_porch=8,
|
||||
.vsync_len=8, .vback_porch=6, .vfront_porch=7
|
||||
},
|
||||
{ // FHD
|
||||
.hactive=1920, .vactive=1080, .pixel_clock=133187,
|
||||
.hsync_len=32, .hback_porch=40, .hfront_porch=8,
|
||||
.vsync_len=8, .vback_porch=6, .vfront_porch=17
|
||||
},
|
||||
};
|
||||
|
||||
void LTDC_IRQHandler() {
|
||||
HAL_LTDC_IRQHandler(&display.hltdc);
|
||||
}
|
||||
|
||||
static void pll_config(int framesize, int refresh) {
|
||||
uint32_t pixel_clock = (display_modes[framesize].pixel_clock * refresh) / 60;
|
||||
|
||||
for (uint32_t divm = 1; divm <= 63; divm++) {
|
||||
for (uint32_t divr = 1; divr <= 128; divr++) {
|
||||
uint32_t vci = 0;
|
||||
uint32_t ref_clk = (HSE_VALUE / 1000) / divm;
|
||||
|
||||
if (1000 <= ref_clk && ref_clk <= 2000) {
|
||||
vci = RCC_PLL3VCIRANGE_0;
|
||||
} else if (2000 <= ref_clk && ref_clk <= 4000) {
|
||||
vci = RCC_PLL3VCIRANGE_1;
|
||||
} else if (4000 <= ref_clk && ref_clk <= 8000) {
|
||||
vci = RCC_PLL3VCIRANGE_2;
|
||||
} else if (8000 <= ref_clk && ref_clk <= 16000) {
|
||||
vci = RCC_PLL3VCIRANGE_3;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t vco = 0;
|
||||
uint32_t pll_clk = pixel_clock * divr;
|
||||
|
||||
if (150000 <= pll_clk && pll_clk <= 420000) {
|
||||
vco = RCC_PLL3VCOMEDIUM;
|
||||
} else if (192000 <= pll_clk && pll_clk <= 836000) {
|
||||
vco = RCC_PLL3VCOWIDE;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t divn = pll_clk / ref_clk;
|
||||
if (divn < 4 || 512 < divn) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t frac = ((pll_clk % ref_clk) * 8192) / ref_clk;
|
||||
|
||||
RCC_PeriphCLKInitTypeDef init;
|
||||
init.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
|
||||
init.PLL3.PLL3M = divm;
|
||||
init.PLL3.PLL3N = divn;
|
||||
init.PLL3.PLL3P = 128;
|
||||
init.PLL3.PLL3Q = 128;
|
||||
init.PLL3.PLL3R = divr;
|
||||
init.PLL3.PLL3RGE = vci;
|
||||
init.PLL3.PLL3VCOSEL = vco;
|
||||
init.PLL3.PLL3FRACN = frac;
|
||||
|
||||
if (HAL_RCCEx_PeriphCLKConfig(&init) == HAL_OK) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Unable to initialize LTDC PLL!"));
|
||||
}
|
||||
|
||||
static void ltdc_init(py_display_obj_t *self) {
|
||||
const display_mode_t *dm = &display_modes[self->framesize];
|
||||
uint32_t fb_size = self->width * self->height * sizeof(uint16_t);
|
||||
|
||||
fb_alloc_mark();
|
||||
for (int i = 0; i < FRAMEBUFFER_COUNT; i++) {
|
||||
self->framebuffers[i] = (uint16_t *) fb_alloc0(fb_size, FB_ALLOC_CACHE_ALIGN);
|
||||
display.framebuffer_layers[i].WindowX0 = 0;
|
||||
display.framebuffer_layers[i].WindowX1 = self->width;
|
||||
display.framebuffer_layers[i].WindowY0 = 0;
|
||||
display.framebuffer_layers[i].WindowY1 = self->height;
|
||||
display.framebuffer_layers[i].PixelFormat = LTDC_PIXEL_FORMAT_RGB565;
|
||||
display.framebuffer_layers[i].Alpha = 0;
|
||||
display.framebuffer_layers[i].Alpha0 = 0;
|
||||
display.framebuffer_layers[i].BlendingFactor1 = LTDC_BLENDING_FACTOR1_PAxCA;
|
||||
display.framebuffer_layers[i].BlendingFactor2 = LTDC_BLENDING_FACTOR2_PAxCA;
|
||||
display.framebuffer_layers[i].FBStartAdress = (uint32_t) self->framebuffers[i];
|
||||
display.framebuffer_layers[i].ImageWidth = self->width;
|
||||
display.framebuffer_layers[i].ImageHeight = self->height;
|
||||
display.framebuffer_layers[i].Backcolor.Blue = 0;
|
||||
display.framebuffer_layers[i].Backcolor.Green = 0;
|
||||
display.framebuffer_layers[i].Backcolor.Red = 0;
|
||||
}
|
||||
fb_alloc_mark_permanent();
|
||||
|
||||
display.hltdc.Instance = LTDC;
|
||||
display.hltdc.Init.HSPolarity = LTDC_HSPOLARITY_AH,
|
||||
display.hltdc.Init.VSPolarity = LTDC_VSPOLARITY_AL,
|
||||
display.hltdc.Init.DEPolarity = LTDC_DEPOLARITY_AL,
|
||||
display.hltdc.Init.PCPolarity = LTDC_PCPOLARITY_IPC,
|
||||
|
||||
display.hltdc.Init.HorizontalSync = dm->hsync_len -1;
|
||||
display.hltdc.Init.VerticalSync = dm->vsync_len - 1;
|
||||
display.hltdc.Init.AccumulatedHBP = dm->hsync_len + dm->hback_porch -1;
|
||||
display.hltdc.Init.AccumulatedVBP = dm->vsync_len + dm->vback_porch -1;
|
||||
display.hltdc.Init.AccumulatedActiveW = dm->hsync_len + dm->hback_porch + self->width -1;
|
||||
display.hltdc.Init.AccumulatedActiveH = dm->vsync_len + dm->vback_porch + self->height -1;
|
||||
display.hltdc.Init.TotalWidth = dm->hsync_len + dm->hback_porch + self->width + dm->hfront_porch - 1;
|
||||
display.hltdc.Init.TotalHeigh = dm->vsync_len + dm->vback_porch + self->height + dm->vfront_porch - 1;
|
||||
|
||||
display.hltdc.Init.Backcolor.Blue = 0;
|
||||
display.hltdc.Init.Backcolor.Green = 0;
|
||||
display.hltdc.Init.Backcolor.Red = 0;
|
||||
HAL_LTDC_Init(&display.hltdc);
|
||||
|
||||
NVIC_SetPriority(LTDC_IRQn, IRQ_PRI_LTDC);
|
||||
HAL_NVIC_EnableIRQ(LTDC_IRQn);
|
||||
|
||||
// Start interrupt chain.
|
||||
HAL_LTDC_ProgramLineEvent(&display.hltdc, 13); // AccumulatedVBP
|
||||
}
|
||||
|
||||
void HAL_LTDC_LineEventCallback(LTDC_HandleTypeDef *hltdc) {
|
||||
py_display_obj_t *self = display.self;
|
||||
|
||||
HAL_LTDC_ConfigLayer_NoReload(&display.hltdc,
|
||||
&display.framebuffer_layers[self->framebuffer_tail], LTDC_LAYER_1);
|
||||
HAL_LTDC_Reload(&display.hltdc, LTDC_RELOAD_VERTICAL_BLANKING);
|
||||
|
||||
#if defined(OMV_DISPLAY_DISP_PIN)
|
||||
if (self->display_on && (self->framebuffer_tail != self->framebuffer_head)) {
|
||||
// Turn display on if there is a new command.
|
||||
omv_gpio_write(OMV_DISPLAY_DISP_PIN, 1);
|
||||
}
|
||||
#endif
|
||||
self->framebuffer_head = self->framebuffer_tail;
|
||||
|
||||
// Continue chain...
|
||||
HAL_LTDC_ProgramLineEvent(&display.hltdc, 13); // AccumulatedVBP
|
||||
}
|
||||
|
||||
static void display_write(py_display_obj_t *self, image_t *src_img, int dst_x_start, int dst_y_start,
|
||||
float x_scale, float y_scale, rectangle_t *roi, int rgb_channel, int alpha,
|
||||
const uint16_t *color_palette, const uint8_t *alpha_palette, image_hint_t hint) {
|
||||
image_t dst_img;
|
||||
dst_img.w = self->width;
|
||||
dst_img.h = self->height;
|
||||
dst_img.pixfmt = PIXFORMAT_RGB565;
|
||||
|
||||
int x0, x1, y0, y1;
|
||||
bool black = !imlib_draw_image_rectangle(&dst_img, src_img, dst_x_start, dst_y_start, x_scale, y_scale,
|
||||
roi, alpha, alpha_palette, hint, &x0, &x1, &y0, &y1);
|
||||
|
||||
// For triple buffering we are never drawing where tail or head (which may instantly update to
|
||||
// to be equal to tail) is.
|
||||
int tail = (self->framebuffer_tail + 1) % FRAMEBUFFER_COUNT;
|
||||
if (tail == self->framebuffer_head) {
|
||||
tail = (tail + 1) % FRAMEBUFFER_COUNT;
|
||||
}
|
||||
dst_img.data = (uint8_t *) self->framebuffers[tail];
|
||||
|
||||
// Set default values for the layer to display the whole framebuffer.
|
||||
display.framebuffer_layers[tail].WindowX0 = black ? 0 : x0;
|
||||
display.framebuffer_layers[tail].WindowX1 = black ? self->width : x1;
|
||||
display.framebuffer_layers[tail].WindowY0 = black ? 0 : y0;
|
||||
display.framebuffer_layers[tail].WindowY1 = black ? self->height : y1;
|
||||
display.framebuffer_layers[tail].Alpha = black ? 0 : fast_roundf((alpha * 255) / 256.f);
|
||||
display.framebuffer_layers[tail].FBStartAdress =
|
||||
black ? ((uint32_t) dst_img.data) : ((uint32_t) (IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&dst_img, y0) + x0));
|
||||
display.framebuffer_layers[tail].ImageWidth = black ? self->width : dst_img.w;
|
||||
display.framebuffer_layers[tail].ImageHeight = black ? self->height : (y1 - y0);
|
||||
|
||||
// Set alpha to 256 here as we will use the layer alpha to blend the image into the background color of black for free.
|
||||
if (!black) {
|
||||
imlib_draw_image(&dst_img, src_img, dst_x_start, dst_y_start,
|
||||
x_scale, y_scale, roi, rgb_channel, 256, color_palette,
|
||||
alpha_palette, hint | IMAGE_HINT_BLACK_BACKGROUND, NULL, NULL);
|
||||
}
|
||||
|
||||
#ifdef __DCACHE_PRESENT
|
||||
// Flush data for DMA
|
||||
if (!black) {
|
||||
SCB_CleanDCache_by_Addr((uint32_t *) dst_img.data, image_size(&dst_img));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Update tail which means a new image is ready.
|
||||
self->framebuffer_tail = tail;
|
||||
}
|
||||
|
||||
static void display_clear(py_display_obj_t *self, bool off) {
|
||||
#if defined(OMV_DISPLAY_DISP_PIN)
|
||||
if (self->display_on && off) {
|
||||
// turns the display off (may not be black)
|
||||
omv_gpio_write(OMV_DISPLAY_DISP_PIN, 0);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// For triple buffering we are never drawing where tail or head (which may instantly update to
|
||||
// to be equal to tail) is.
|
||||
int tail = (self->framebuffer_tail + 1) % FRAMEBUFFER_COUNT;
|
||||
if (tail == self->framebuffer_head) {
|
||||
tail = (tail + 1) % FRAMEBUFFER_COUNT;
|
||||
}
|
||||
|
||||
// Set default values for the layer to display the whole framebuffer.
|
||||
display.framebuffer_layers[tail].WindowX0 = 0;
|
||||
display.framebuffer_layers[tail].WindowX1 = self->width;
|
||||
display.framebuffer_layers[tail].WindowY0 = 0;
|
||||
display.framebuffer_layers[tail].WindowY1 = self->height;
|
||||
display.framebuffer_layers[tail].Alpha = 0;
|
||||
display.framebuffer_layers[tail].FBStartAdress = (uint32_t) self->framebuffers[tail];
|
||||
display.framebuffer_layers[tail].ImageWidth = self->width;
|
||||
display.framebuffer_layers[tail].ImageHeight = self->height;
|
||||
|
||||
// Update tail which means a new image is ready.
|
||||
self->framebuffer_tail = tail;
|
||||
}
|
||||
|
||||
#ifdef OMV_DISPLAY_BL_PIN
|
||||
static void display_set_backlight(py_display_obj_t *self, uint32_t intensity) {
|
||||
#ifdef OMV_DISPLAY_BL_TIM
|
||||
if ((self->intensity < 255) && (255 <= intensity)) {
|
||||
#else
|
||||
if ((self->intensity < 1) && (1 <= intensity)) {
|
||||
#endif
|
||||
omv_gpio_config(OMV_DISPLAY_BL_PIN, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1);
|
||||
omv_gpio_write(OMV_DISPLAY_BL_PIN, 1);
|
||||
} else if ((0 < self->intensity) && (intensity <= 0)) {
|
||||
omv_gpio_write(OMV_DISPLAY_BL_PIN, 0);
|
||||
omv_gpio_deinit(OMV_DISPLAY_BL_PIN);
|
||||
}
|
||||
|
||||
#ifdef OMV_DISPLAY_BL_TIM
|
||||
int tclk = OMV_DISPLAY_BL_TIM_PCLK_FREQ() * 2;
|
||||
int period = (tclk / OMV_DISPLAY_BL_TIM_FREQ) - 1;
|
||||
|
||||
if (((self->intensity <= 0) || (255 <= self->intensity)) && (0 < intensity) && (intensity < 255)) {
|
||||
omv_gpio_config(OMV_DISPLAY_BL_PIN, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1);
|
||||
|
||||
display.htim.Instance = OMV_DISPLAY_BL_TIM;
|
||||
display.htim.Init.Prescaler = 0;
|
||||
display.htim.Init.CounterMode = TIM_COUNTERMODE_UP;
|
||||
display.htim.Init.Period = period;
|
||||
display.htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
|
||||
display.htim.Init.RepetitionCounter = 0;
|
||||
display.htim.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
|
||||
|
||||
TIM_OC_InitTypeDef lcd_tim_oc_handle;
|
||||
lcd_tim_oc_handle.Pulse = (period * intensity) / 255;
|
||||
lcd_tim_oc_handle.OCMode = TIM_OCMODE_PWM1;
|
||||
lcd_tim_oc_handle.OCPolarity = TIM_OCPOLARITY_HIGH;
|
||||
lcd_tim_oc_handle.OCNPolarity = TIM_OCNPOLARITY_HIGH;
|
||||
lcd_tim_oc_handle.OCFastMode = TIM_OCFAST_DISABLE;
|
||||
lcd_tim_oc_handle.OCIdleState = TIM_OCIDLESTATE_RESET;
|
||||
lcd_tim_oc_handle.OCNIdleState = TIM_OCNIDLESTATE_RESET;
|
||||
|
||||
HAL_TIM_PWM_Init(&display.htim);
|
||||
HAL_TIM_PWM_ConfigChannel(&display.htim, &lcd_tim_oc_handle, OMV_DISPLAY_BL_TIM_CHANNEL);
|
||||
HAL_TIM_PWM_Start(&display.htim, OMV_DISPLAY_BL_TIM_CHANNEL);
|
||||
} else if ((0 < self->intensity) && (self->intensity < 255) && ((intensity <= 0) || (255 <= intensity))) {
|
||||
HAL_TIM_PWM_Stop(&display.htim, OMV_DISPLAY_BL_TIM_CHANNEL);
|
||||
HAL_TIM_PWM_DeInit(&display.htim);
|
||||
} else if ((0 < self->intensity) && (self->intensity < 255) && (0 < intensity) && (intensity < 255)) {
|
||||
__HAL_TIM_SET_COMPARE(&display.htim, OMV_DISPLAY_BL_TIM_CHANNEL, (period * intensity) / 255);
|
||||
}
|
||||
#endif
|
||||
|
||||
self->intensity = intensity;
|
||||
}
|
||||
#endif // OMV_DISPLAY_BL_PIN
|
||||
|
||||
static void display_deinit(py_display_obj_t *self) {
|
||||
HAL_LTDC_DeInit(&display.hltdc);
|
||||
HAL_NVIC_DisableIRQ(LTDC_IRQn);
|
||||
|
||||
__HAL_RCC_PLL3_DISABLE();
|
||||
uint32_t tickstart = mp_hal_ticks_ms();
|
||||
while (__HAL_RCC_GET_FLAG(RCC_FLAG_PLL3RDY)) {
|
||||
if ((mp_hal_ticks_ms() - tickstart) > PLL_TIMEOUT_VALUE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fb_alloc_free_till_mark_past_mark_permanent();
|
||||
|
||||
#ifdef OMV_DISPLAY_BL_PIN
|
||||
if (self->display_on) {
|
||||
// back to default state
|
||||
display_set_backlight(self, 0);
|
||||
}
|
||||
#endif // OMV_DISPLAY_BL_PIN
|
||||
}
|
||||
|
||||
mp_obj_t display_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
||||
enum { ARG_framesize, ARG_refresh, ARG_display_on, ARG_triple_buffer, ARG_portrait };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_framesize, MP_ARG_INT, {.u_int = DISPLAY_RESOLUTION_FWVGA } },
|
||||
{ MP_QSTR_refresh, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 60 } },
|
||||
{ MP_QSTR_display_on, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} },
|
||||
{ MP_QSTR_triple_buffer, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} },
|
||||
{ MP_QSTR_portrait, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} },
|
||||
};
|
||||
|
||||
// Parse args.
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
if ((args[ARG_framesize].u_int < 0) || (args[ARG_framesize].u_int >= DISPLAY_RESOLUTION_MAX)) {
|
||||
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Invalid Frame Size!"));
|
||||
}
|
||||
if ((args[ARG_refresh].u_int < 30) || (args[ARG_refresh].u_int > 120)) {
|
||||
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Invalid Refresh Rate!"));
|
||||
}
|
||||
|
||||
py_display_obj_t *self = (py_display_obj_t *) m_new_obj_with_finaliser(py_display_obj_t);
|
||||
self->base.type = &py_rgb_display_type;
|
||||
self->refresh = args[ARG_refresh].u_int;
|
||||
self->display_on = args[ARG_display_on].u_bool;
|
||||
self->bgr = false;
|
||||
self->triple_buffer = args[ARG_triple_buffer].u_bool;
|
||||
self->framebuffer_tail = 0;
|
||||
self->framebuffer_head = 0;
|
||||
self->framesize = args[ARG_framesize].u_int;
|
||||
if (args[ARG_portrait].u_bool) {
|
||||
self->width = display_modes[self->framesize].vactive;
|
||||
self->height = display_modes[self->framesize].hactive;
|
||||
} else {
|
||||
self->width = display_modes[self->framesize].hactive;
|
||||
self->height = display_modes[self->framesize].vactive;
|
||||
}
|
||||
|
||||
// Store state to access it from IRQ handlers or callbacks
|
||||
display.self = self;
|
||||
|
||||
// Configure PLL3 for the selected mode clock.
|
||||
pll_config(self->framesize, self->refresh);
|
||||
|
||||
// Init LTDC controller
|
||||
ltdc_init(self);
|
||||
|
||||
#ifdef OMV_DISPLAY_BL_PIN
|
||||
if (self->display_on) {
|
||||
display_set_backlight(self, 255); // to on state
|
||||
}
|
||||
#endif // OMV_DISPLAY_BL_PIN
|
||||
|
||||
return MP_OBJ_FROM_PTR(self);
|
||||
}
|
||||
|
||||
STATIC const py_display_p_t py_display_p = {
|
||||
.deinit = display_deinit,
|
||||
.clear = display_clear,
|
||||
.write = display_write,
|
||||
#ifdef OMV_DISPLAY_BL_PIN
|
||||
.set_backlight = display_set_backlight,
|
||||
#endif
|
||||
};
|
||||
|
||||
MP_DEFINE_CONST_OBJ_TYPE(
|
||||
py_rgb_display_type,
|
||||
MP_QSTR_RGBDisplay,
|
||||
MP_TYPE_FLAG_NONE,
|
||||
make_new, display_make_new,
|
||||
protocol, &py_display_p,
|
||||
locals_dict, &py_display_locals_dict
|
||||
);
|
||||
#endif // MICROPY_PY_DISPLAY
|
||||
@ -289,9 +289,9 @@ void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim) {
|
||||
}
|
||||
#endif // (OMV_XCLK_SOURCE == OMV_XCLK_TIM)
|
||||
|
||||
#if defined(OMV_LCD_BL_TIM)
|
||||
if (htim->Instance == OMV_LCD_BL_TIM) {
|
||||
OMV_LCD_BL_TIM_CLK_ENABLE();
|
||||
#if defined(OMV_DISPLAY_BL_TIM)
|
||||
if (htim->Instance == OMV_DISPLAY_BL_TIM) {
|
||||
OMV_DISPLAY_BL_TIM_CLK_ENABLE();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -303,11 +303,11 @@ void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim) {
|
||||
}
|
||||
|
||||
void HAL_TIM_PWM_MspDeInit(TIM_HandleTypeDef *htim) {
|
||||
#if defined(OMV_LCD_BL_TIM)
|
||||
if (htim->Instance == OMV_LCD_BL_TIM) {
|
||||
OMV_LCD_BL_TIM_FORCE_RESET();
|
||||
OMV_LCD_BL_TIM_RELEASE_RESET();
|
||||
OMV_LCD_BL_TIM_CLK_DISABLE();
|
||||
#if defined(OMV_DISPLAY_BL_TIM)
|
||||
if (htim->Instance == OMV_DISPLAY_BL_TIM) {
|
||||
OMV_DISPLAY_BL_TIM_FORCE_RESET();
|
||||
OMV_DISPLAY_BL_TIM_RELEASE_RESET();
|
||||
OMV_DISPLAY_BL_TIM_CLK_DISABLE();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -601,109 +601,128 @@ void HAL_JPEG_MspDeInit(JPEG_HandleTypeDef *hjpeg) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(OMV_LCD_CONTROLLER) && (!defined(OMV_DSI_CONTROLLER))
|
||||
static const omv_gpio_t ltdc_pins[] = {
|
||||
OMV_LCD_R0_PIN,
|
||||
OMV_LCD_R1_PIN,
|
||||
OMV_LCD_R2_PIN,
|
||||
OMV_LCD_R3_PIN,
|
||||
OMV_LCD_R4_PIN,
|
||||
OMV_LCD_R5_PIN,
|
||||
OMV_LCD_R6_PIN,
|
||||
OMV_LCD_R7_PIN,
|
||||
OMV_LCD_G0_PIN,
|
||||
OMV_LCD_G1_PIN,
|
||||
OMV_LCD_G2_PIN,
|
||||
OMV_LCD_G3_PIN,
|
||||
OMV_LCD_G4_PIN,
|
||||
OMV_LCD_G5_PIN,
|
||||
OMV_LCD_G6_PIN,
|
||||
OMV_LCD_G7_PIN,
|
||||
OMV_LCD_B0_PIN,
|
||||
OMV_LCD_B1_PIN,
|
||||
OMV_LCD_B2_PIN,
|
||||
OMV_LCD_B3_PIN,
|
||||
OMV_LCD_B4_PIN,
|
||||
OMV_LCD_B5_PIN,
|
||||
OMV_LCD_B6_PIN,
|
||||
OMV_LCD_B7_PIN,
|
||||
OMV_LCD_CLK_PIN,
|
||||
OMV_LCD_DE_PIN,
|
||||
OMV_LCD_HSYNC_PIN,
|
||||
OMV_LCD_VSYNC_PIN,
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(OMV_LCD_CONTROLLER) || defined(OMV_DSI_CONTROLLER)
|
||||
#if defined(OMV_RGB_DISPLAY_CONTROLLER)
|
||||
void HAL_LTDC_MspInit(LTDC_HandleTypeDef *hltdc) {
|
||||
#if defined(OMV_DSI_CONTROLLER)
|
||||
if (hltdc->Instance == OMV_LCD_CONTROLLER) {
|
||||
OMV_LCD_CLK_ENABLE();
|
||||
}
|
||||
#elif defined(OMV_LCD_CONTROLLER)
|
||||
if (hltdc->Instance == OMV_LCD_CONTROLLER) {
|
||||
OMV_LCD_CLK_ENABLE();
|
||||
#if defined(OMV_RGB_DISPLAY_R0_PIN)
|
||||
const omv_gpio_t ltdc_pins[] = {
|
||||
OMV_RGB_DISPLAY_R0_PIN,
|
||||
OMV_RGB_DISPLAY_R1_PIN,
|
||||
OMV_RGB_DISPLAY_R2_PIN,
|
||||
OMV_RGB_DISPLAY_R3_PIN,
|
||||
OMV_RGB_DISPLAY_R4_PIN,
|
||||
OMV_RGB_DISPLAY_R5_PIN,
|
||||
OMV_RGB_DISPLAY_R6_PIN,
|
||||
OMV_RGB_DISPLAY_R7_PIN,
|
||||
OMV_RGB_DISPLAY_G0_PIN,
|
||||
OMV_RGB_DISPLAY_G1_PIN,
|
||||
OMV_RGB_DISPLAY_G2_PIN,
|
||||
OMV_RGB_DISPLAY_G3_PIN,
|
||||
OMV_RGB_DISPLAY_G4_PIN,
|
||||
OMV_RGB_DISPLAY_G5_PIN,
|
||||
OMV_RGB_DISPLAY_G6_PIN,
|
||||
OMV_RGB_DISPLAY_G7_PIN,
|
||||
OMV_RGB_DISPLAY_B0_PIN,
|
||||
OMV_RGB_DISPLAY_B1_PIN,
|
||||
OMV_RGB_DISPLAY_B2_PIN,
|
||||
OMV_RGB_DISPLAY_B3_PIN,
|
||||
OMV_RGB_DISPLAY_B4_PIN,
|
||||
OMV_RGB_DISPLAY_B5_PIN,
|
||||
OMV_RGB_DISPLAY_B6_PIN,
|
||||
OMV_RGB_DISPLAY_B7_PIN,
|
||||
OMV_RGB_DISPLAY_CLK_PIN,
|
||||
OMV_RGB_DISPLAY_DE_PIN,
|
||||
OMV_RGB_DISPLAY_HSYNC_PIN,
|
||||
OMV_RGB_DISPLAY_VSYNC_PIN,
|
||||
};
|
||||
#endif
|
||||
|
||||
OMV_RGB_DISPLAY_CLK_ENABLE();
|
||||
|
||||
#if defined(OMV_RGB_DISPLAY_R0_PIN)
|
||||
for (int i = 0; i < OMV_ARRAY_SIZE(ltdc_pins); i++) {
|
||||
omv_gpio_config(ltdc_pins[i], OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_MAX, -1);
|
||||
}
|
||||
|
||||
#if defined(OMV_LCD_DISP_PIN)
|
||||
omv_gpio_config(OMV_LCD_DISP_PIN, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1);
|
||||
omv_gpio_write(OMV_LCD_DISP_PIN, 0);
|
||||
#endif
|
||||
|
||||
#if defined(OMV_LCD_BL_PIN)
|
||||
omv_gpio_config(OMV_LCD_BL_PIN, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1);
|
||||
omv_gpio_write(OMV_LCD_BL_PIN, 0);
|
||||
#if defined(OMV_RGB_DISPLAY_DISP_PIN)
|
||||
omv_gpio_config(OMV_RGB_DISPLAY_DISP_PIN, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1);
|
||||
omv_gpio_write(OMV_RGB_DISPLAY_DISP_PIN, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(OMV_RGB_DISPLAY_BL_PIN)
|
||||
omv_gpio_config(OMV_RGB_DISPLAY_BL_PIN, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1);
|
||||
omv_gpio_write(OMV_RGB_DISPLAY_BL_PIN, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void HAL_LTDC_MspDeInit(LTDC_HandleTypeDef *hltdc) {
|
||||
#if defined(OMV_DSI_CONTROLLER)
|
||||
if (hltdc->Instance == OMV_LCD_CONTROLLER) {
|
||||
OMV_LCD_FORCE_RESET();
|
||||
OMV_LCD_RELEASE_RESET();
|
||||
OMV_LCD_CLK_DISABLE();
|
||||
}
|
||||
#elif defined(OMV_LCD_CONTROLLER)
|
||||
if (hltdc->Instance == OMV_LCD_CONTROLLER) {
|
||||
OMV_LCD_FORCE_RESET();
|
||||
OMV_LCD_RELEASE_RESET();
|
||||
OMV_LCD_CLK_DISABLE();
|
||||
#if defined(OMV_RGB_DISPLAY_R0_PIN)
|
||||
const omv_gpio_t ltdc_pins[] = {
|
||||
OMV_RGB_DISPLAY_R0_PIN,
|
||||
OMV_RGB_DISPLAY_R1_PIN,
|
||||
OMV_RGB_DISPLAY_R2_PIN,
|
||||
OMV_RGB_DISPLAY_R3_PIN,
|
||||
OMV_RGB_DISPLAY_R4_PIN,
|
||||
OMV_RGB_DISPLAY_R5_PIN,
|
||||
OMV_RGB_DISPLAY_R6_PIN,
|
||||
OMV_RGB_DISPLAY_R7_PIN,
|
||||
OMV_RGB_DISPLAY_G0_PIN,
|
||||
OMV_RGB_DISPLAY_G1_PIN,
|
||||
OMV_RGB_DISPLAY_G2_PIN,
|
||||
OMV_RGB_DISPLAY_G3_PIN,
|
||||
OMV_RGB_DISPLAY_G4_PIN,
|
||||
OMV_RGB_DISPLAY_G5_PIN,
|
||||
OMV_RGB_DISPLAY_G6_PIN,
|
||||
OMV_RGB_DISPLAY_G7_PIN,
|
||||
OMV_RGB_DISPLAY_B0_PIN,
|
||||
OMV_RGB_DISPLAY_B1_PIN,
|
||||
OMV_RGB_DISPLAY_B2_PIN,
|
||||
OMV_RGB_DISPLAY_B3_PIN,
|
||||
OMV_RGB_DISPLAY_B4_PIN,
|
||||
OMV_RGB_DISPLAY_B5_PIN,
|
||||
OMV_RGB_DISPLAY_B6_PIN,
|
||||
OMV_RGB_DISPLAY_B7_PIN,
|
||||
OMV_RGB_DISPLAY_CLK_PIN,
|
||||
OMV_RGB_DISPLAY_DE_PIN,
|
||||
OMV_RGB_DISPLAY_HSYNC_PIN,
|
||||
OMV_RGB_DISPLAY_VSYNC_PIN,
|
||||
};
|
||||
#endif
|
||||
|
||||
OMV_RGB_DISPLAY_FORCE_RESET();
|
||||
OMV_RGB_DISPLAY_RELEASE_RESET();
|
||||
OMV_RGB_DISPLAY_CLK_DISABLE();
|
||||
|
||||
#if defined(OMV_RGB_DISPLAY_R0_PIN)
|
||||
for (int i = 0; i < OMV_ARRAY_SIZE(ltdc_pins); i++) {
|
||||
omv_gpio_deinit(ltdc_pins[i]);
|
||||
}
|
||||
|
||||
#if defined(OMV_LCD_DISP_PIN)
|
||||
omv_gpio_deinit(OMV_LCD_DISP_PIN);
|
||||
#endif
|
||||
|
||||
#if defined(OMV_LCD_BL_PIN)
|
||||
omv_gpio_deinit(OMV_LCD_BL_PIN);
|
||||
#if defined(OMV_RGB_DISPLAY_DISP_PIN)
|
||||
omv_gpio_deinit(OMV_RGB_DISPLAY_DISP_PIN);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(OMV_RGB_DISPLAY_BL_PIN)
|
||||
omv_gpio_deinit(OMV_RGB_DISPLAY_BL_PIN);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void HAL_DAC_MspInit(DAC_HandleTypeDef *hdac) {
|
||||
#if defined(OMV_SPI_LCD_BL_DAC)
|
||||
if (hdac->Instance == OMV_SPI_LCD_BL_DAC) {
|
||||
OMV_SPI_LCD_BL_DAC_CLK_ENABLE();
|
||||
#if defined(OMV_DISPLAY_BL_DAC)
|
||||
if (hdac->Instance == OMV_DISPLAY_BL_DAC) {
|
||||
OMV_DISPLAY_BL_DAC_CLK_ENABLE();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void HAL_DAC_MspDeInit(DAC_HandleTypeDef *hdac) {
|
||||
#if defined(OMV_SPI_LCD_BL_DAC)
|
||||
if (hdac->Instance == OMV_SPI_LCD_BL_DAC) {
|
||||
OMV_SPI_LCD_BL_DAC_FORCE_RESET();
|
||||
OMV_SPI_LCD_BL_DAC_RELEASE_RESET();
|
||||
OMV_SPI_LCD_BL_DAC_CLK_DISABLE();
|
||||
#if defined(OMV_DISPLAY_BL_DAC)
|
||||
if (hdac->Instance == OMV_DISPLAY_BL_DAC) {
|
||||
OMV_DISPLAY_BL_DAC_FORCE_RESET();
|
||||
OMV_DISPLAY_BL_DAC_RELEASE_RESET();
|
||||
OMV_DISPLAY_BL_DAC_CLK_DISABLE();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user