mirror of
https://github.com/openmv/openmv.git
synced 2025-09-26 23:09:13 +08:00
320 lines
16 KiB
C
320 lines
16 KiB
C
/*
|
|
* SPDX-License-Identifier: MIT
|
|
*
|
|
* Copyright (C) 2013-2024 OpenMV, LLC.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*
|
|
* 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);
|
|
}
|
|
if (self->bl_controller != mp_const_none) {
|
|
mp_obj_t dest[2];
|
|
mp_load_method_maybe(self->bl_controller, MP_QSTR_deinit, dest);
|
|
if (dest[0] != MP_OBJ_NULL) {
|
|
mp_call_method_n_kw(0, 0, dest);
|
|
}
|
|
}
|
|
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(size_t 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(size_t 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 > 100) {
|
|
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Intensity ranges between 0 (off) and 100 (full on)"));
|
|
}
|
|
if (self->bl_controller != mp_const_none) {
|
|
// If the display has a backlight controller set, call it first.
|
|
mp_obj_t dest[3];
|
|
mp_load_method(self->bl_controller, MP_QSTR_backlight, dest);
|
|
dest[2] = args[1];
|
|
mp_call_method_n_kw(1, 0, dest);
|
|
} else {
|
|
// Otherwise, if the display protocol's set_backlight is set, call it.
|
|
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 {
|
|
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Display does not support backlight control."));
|
|
}
|
|
}
|
|
self->intensity = 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(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
|
enum {
|
|
ARG_image, ARG_x, ARG_y, ARG_x_scale, ARG_y_scale, ARG_roi,
|
|
ARG_channel, ARG_alpha, ARG_color_palette, ARG_alpha_palette, ARG_hint
|
|
};
|
|
static const mp_arg_t allowed_args[] = {
|
|
{ MP_QSTR_image, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE} },
|
|
{ MP_QSTR_x, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0 } },
|
|
{ MP_QSTR_y, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0 } },
|
|
{ MP_QSTR_x_scale, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
|
|
{ MP_QSTR_y_scale, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
|
|
{ MP_QSTR_roi, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
|
|
{ MP_QSTR_rgb_channel, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = -1 } },
|
|
{ MP_QSTR_alpha, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 255 } },
|
|
{ MP_QSTR_color_palette, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
|
|
{ MP_QSTR_alpha_palette, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
|
|
{ MP_QSTR_hint, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0 } },
|
|
};
|
|
|
|
// Parse args.
|
|
py_display_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
|
|
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
|
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
|
|
|
fb_alloc_mark();
|
|
image_t *image = py_helper_arg_to_image(args[ARG_image].u_obj, ARG_IMAGE_ANY | ARG_IMAGE_ALLOC);
|
|
rectangle_t roi = py_helper_arg_to_roi(args[ARG_roi].u_obj, image);
|
|
|
|
if (args[ARG_channel].u_int < -1 || args[ARG_channel].u_int > 2) {
|
|
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("RGB channel can be 0, 1, or 2"));
|
|
}
|
|
|
|
if (args[ARG_alpha].u_int < 0 || args[ARG_alpha].u_int > 255) {
|
|
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Alpha ranges between 0 and 255"));
|
|
}
|
|
|
|
float x_scale = 1.0f;
|
|
float y_scale = 1.0f;
|
|
py_helper_arg_to_scale(args[ARG_x_scale].u_obj, args[ARG_y_scale].u_obj, &x_scale, &y_scale);
|
|
|
|
if (y_scale < 0 && !self->triple_buffer) {
|
|
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Vertical flip requires triple buffering!"));
|
|
}
|
|
|
|
const uint16_t *color_palette = py_helper_arg_to_palette(args[ARG_color_palette].u_obj, PIXFORMAT_RGB565);
|
|
const uint8_t *alpha_palette = py_helper_arg_to_palette(args[ARG_alpha_palette].u_obj, PIXFORMAT_GRAYSCALE);
|
|
|
|
py_display_p_t *display_p = (py_display_p_t *) MP_OBJ_TYPE_GET_SLOT(self->base.type, protocol);
|
|
display_p->write(self, image, args[ARG_x].u_int, args[ARG_y].u_int, x_scale, y_scale, &roi,
|
|
args[ARG_channel].u_int, args[ARG_alpha].u_int, color_palette, alpha_palette, args[ARG_hint].u_int);
|
|
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 mp_obj_t py_display_bus_write(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
|
enum { ARG_cmd, ARG_args, ARG_dcs };
|
|
static const mp_arg_t allowed_args[] = {
|
|
{ MP_QSTR_cmd, MP_ARG_INT | MP_ARG_REQUIRED },
|
|
{ MP_QSTR_args, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
|
|
{ MP_QSTR_dcs, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_bool = false } },
|
|
};
|
|
|
|
// Parse args.
|
|
py_display_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
|
|
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
|
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
|
|
|
py_display_p_t *display_p = (py_display_p_t *) MP_OBJ_TYPE_GET_SLOT(self->base.type, protocol);
|
|
if (display_p->bus_write == NULL) {
|
|
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Expected a display controller"));
|
|
} else if (args[ARG_args].u_obj == mp_const_none) {
|
|
display_p->bus_write(self, args[ARG_cmd].u_int, NULL, 0, args[ARG_dcs].u_bool);
|
|
} else if (mp_obj_is_int(args[ARG_args].u_obj)) {
|
|
uint8_t arg = mp_obj_get_int(args[ARG_args].u_obj);
|
|
display_p->bus_write(self, args[ARG_cmd].u_int, &arg, 1, args[ARG_dcs].u_bool);
|
|
} else {
|
|
mp_buffer_info_t rbuf;
|
|
mp_get_buffer_raise(args[ARG_args].u_obj, &rbuf, MP_BUFFER_READ);
|
|
display_p->bus_write(self, args[ARG_cmd].u_int, rbuf.buf, rbuf.len, args[ARG_dcs].u_bool);
|
|
}
|
|
return mp_const_none;
|
|
}
|
|
static MP_DEFINE_CONST_FUN_OBJ_KW(py_display_bus_write_obj, 1, py_display_bus_write);
|
|
|
|
static mp_obj_t py_display_bus_read(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
|
enum { ARG_cmd, ARG_len, ARG_args, ARG_dcs };
|
|
static const mp_arg_t allowed_args[] = {
|
|
{ MP_QSTR_cmd, MP_ARG_INT | MP_ARG_REQUIRED },
|
|
{ MP_QSTR_len, MP_ARG_INT | MP_ARG_REQUIRED },
|
|
{ MP_QSTR_args, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
|
|
{ MP_QSTR_dcs, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_bool = false } },
|
|
};
|
|
|
|
// Parse args.
|
|
py_display_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
|
|
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
|
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
|
|
|
py_display_p_t *display_p = (py_display_p_t *) MP_OBJ_TYPE_GET_SLOT(self->base.type, protocol);
|
|
mp_obj_array_t *wbuf = MP_OBJ_TO_PTR(mp_obj_new_bytearray_by_ref(args[ARG_len].u_int,
|
|
m_new(byte, args[ARG_len].u_int)));
|
|
if (display_p->bus_read == NULL) {
|
|
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Expected a display controller"));
|
|
} else if (args[ARG_args].u_obj == mp_const_none) {
|
|
display_p->bus_read(self, args[ARG_cmd].u_int, NULL, 0, wbuf->items, wbuf->len, args[ARG_dcs].u_bool);
|
|
} else if (mp_obj_is_int(args[ARG_args].u_obj)) {
|
|
uint8_t arg = mp_obj_get_int(args[ARG_args].u_obj);
|
|
display_p->bus_read(self, args[ARG_cmd].u_int, &arg, 1, wbuf->items, wbuf->len, args[ARG_dcs].u_bool);
|
|
} else {
|
|
mp_buffer_info_t rbuf;
|
|
mp_get_buffer_raise(args[ARG_args].u_obj, &rbuf, MP_BUFFER_READ);
|
|
display_p->bus_read(self, args[ARG_cmd].u_int, rbuf.buf, rbuf.len,
|
|
wbuf->items, wbuf->len, args[ARG_dcs].u_bool);
|
|
}
|
|
return MP_OBJ_FROM_PTR(wbuf);
|
|
}
|
|
static MP_DEFINE_CONST_FUN_OBJ_KW(py_display_bus_read_obj, 1, py_display_bus_read);
|
|
|
|
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_ROM_QSTR(MP_QSTR_bus_write), MP_ROM_PTR(&py_display_bus_write_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_bus_read), MP_ROM_PTR(&py_display_bus_read_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_EXTENSIBLE_MODULE(MP_QSTR_display, display_module);
|
|
#endif // MICROPY_PY_DISPLAY
|