ports/stm32: Enable DSI display support.

This commit is contained in:
iabdalkader 2023-09-29 20:57:52 +02:00
parent 6c63368297
commit 55dfec4f06
6 changed files with 363 additions and 70 deletions

View File

@ -376,6 +376,15 @@ void SystemClock_Config(void)
PeriphClkInitStruct.Spi45ClockSelection = RCC_SPI45CLKSOURCE_PLL2;
#endif
#if defined(DSI)
PeriphClkInitStruct.PeriphClockSelection |= RCC_PERIPHCLK_DSI;
#if defined(OMV_OSC_DSI_CLKSOURCE)
PeriphClkInitStruct.DsiClockSelection = OMV_OSC_DSI_CLKSOURCE;
#else
PeriphClkInitStruct.DsiClockSelection = RCC_DSICLKSOURCE_PHY;
#endif // defined(OMV_OSC_DSI_CLKSOURCE)
#endif // defined(DSI)
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) {
// Initialization Error
__fatal_error("HAL_RCCEx_PeriphCLKConfig");

View File

@ -11,6 +11,7 @@ stm32h7xx_hal_adc.c\
stm32h7xx_hal_adc_ex.c\
stm32h7xx_hal_dac.c\
stm32h7xx_hal_dac_ex.c\
stm32h7xx_hal_dsi.c\
stm32h7xx_hal.c\
stm32h7xx_hal_cortex.c\
stm32h7xx_hal_crc.c\

View File

@ -185,6 +185,72 @@ STATIC mp_obj_t py_display_write(uint n_args, const mp_obj_t *args, mp_map_t *kw
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_display_write_obj, 2, py_display_write);
#ifdef OMV_DSI_DISPLAY_CONTROLLER
STATIC mp_obj_t py_display_dsi_write(uint 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->dsi_write == NULL) {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Expected a DSI display controller"));
} else if (args[ARG_args].u_obj == mp_const_none) {
display_p->dsi_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->dsi_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->dsi_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_dsi_write_obj, 1, py_display_dsi_write);
STATIC mp_obj_t py_display_dsi_read(uint 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->dsi_read == NULL) {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Expected a DSI display controller"));
} else if (args[ARG_args].u_obj == mp_const_none) {
display_p->dsi_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->dsi_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->dsi_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_dsi_read_obj, 1, py_display_dsi_read);
#endif
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) },
@ -198,6 +264,10 @@ STATIC const mp_rom_map_elem_t py_display_locals_dict_table[] = {
{ 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) },
#ifdef OMV_DSI_DISPLAY_CONTROLLER
{ MP_ROM_QSTR(MP_QSTR_dsi_write), MP_ROM_PTR(&py_display_dsi_write_obj) },
{ MP_ROM_QSTR(MP_QSTR_dsi_read), MP_ROM_PTR(&py_display_dsi_read_obj) },
#endif
};
MP_DEFINE_CONST_DICT(py_display_locals_dict, py_display_locals_dict_table);
@ -242,5 +312,9 @@ const mp_obj_module_t display_module = {
.globals = (mp_obj_t) &globals_dict,
};
MP_REGISTER_MODULE(MP_QSTR_display, display_module);
#ifdef MP_REGISTER_EXTENSIBLE_MODULE
MP_REGISTER_EXTENSIBLE_MODULE(MP_QSTR_display, display_module);
#else
MP_REGISTER_MODULE(MP_QSTR_udisplay, display_module);
#endif
#endif // MICROPY_PY_DISPLAY

View File

@ -41,6 +41,7 @@ typedef enum {
typedef struct _py_display_obj_t {
mp_obj_base_t base;
uint32_t vcid;
uint32_t width;
uint32_t height;
uint32_t framesize;
@ -49,6 +50,7 @@ typedef struct _py_display_obj_t {
bool bgr;
bool byte_swap;
bool display_on;
mp_obj_t controller;
#if defined(OMV_SPI_DISPLAY_CONTROLLER)
omv_spi_t spi_bus;
bool spi_tx_running;
@ -68,6 +70,11 @@ typedef struct _py_display_p_t {
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);
#ifdef OMV_DSI_DISPLAY_CONTROLLER
// To be implemented by MIPI DSI controllers.
int (*dsi_write) (py_display_obj_t *self, uint8_t cmd, uint8_t *args, size_t n_args, bool dcs);
int (*dsi_read) (py_display_obj_t *self, uint8_t cmd, uint8_t *args, size_t n_args, uint8_t *buf, size_t len, bool dcs);
#endif
} py_display_p_t;
extern const mp_obj_type_t py_spi_display_type;

View File

@ -6,11 +6,13 @@
*
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* LTDC display Python module.
* LTDC/DSI display Python module.
*/
#include "omv_boardconfig.h"
#if MICROPY_PY_DISPLAY && defined(OMV_RGB_DISPLAY_CONTROLLER)
#if MICROPY_PY_DISPLAY && \
(defined(OMV_RGB_DISPLAY_CONTROLLER) || \
defined(OMV_DSI_DISPLAY_CONTROLLER))
#include "py/obj.h"
#include "py/runtime.h"
@ -21,11 +23,15 @@
#include "omv_gpio.h"
#include "py_display.h"
#if defined(OMV_RGB_DISPLAY_BL_PIN)
#if defined(OMV_DSI_DISPLAY_BL_PIN)
#define OMV_DISPLAY_BL_PIN OMV_DSI_DISPLAY_BL_PIN
#elif defined(OMV_RGB_DISPLAY_BL_PIN)
#define OMV_DISPLAY_BL_PIN OMV_RGB_DISPLAY_BL_PIN
#endif
#if defined(OMV_RGB_DISPLAY_DISP_PIN)
#if defined(OMV_DSI_DISPLAY_DISP_PIN)
#define OMV_DISPLAY_DISP_PIN OMV_DSI_DISPLAY_DISP_PIN
#elif defined(OMV_RGB_DISPLAY_DISP_PIN)
#define OMV_DISPLAY_DISP_PIN OMV_RGB_DISPLAY_DISP_PIN
#endif
@ -46,6 +52,9 @@ typedef struct display_mode {
typedef struct _display_state {
py_display_obj_t *self;
LTDC_HandleTypeDef hltdc;
#ifdef OMV_DSI_DISPLAY_CONTROLLER
DSI_HandleTypeDef hdsi;
#endif
#ifdef OMV_DISPLAY_BL_TIM
TIM_HandleTypeDef htim;
#endif
@ -56,94 +65,94 @@ 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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
},
};
@ -151,6 +160,12 @@ void LTDC_IRQHandler() {
HAL_LTDC_IRQHandler(&display.hltdc);
}
#ifdef OMV_DSI_DISPLAY_CONTROLLER
void DSI_IRQHandler(void) {
HAL_DSI_IRQHandler(&display.hdsi);
}
#endif
static void pll_config(int framesize, int refresh) {
uint32_t pixel_clock = (display_modes[framesize].pixel_clock * refresh) / 60;
@ -209,6 +224,97 @@ static void pll_config(int framesize, int refresh) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Unable to initialize LTDC PLL!"));
}
#ifdef OMV_DSI_DISPLAY_CONTROLLER
static void dsi_init(py_display_obj_t *self) {
const display_mode_t *dm = &display_modes[self->framesize];
DSI_PLLInitTypeDef dsi_pllinit;
dsi_pllinit.PLLNDIV = 125;
dsi_pllinit.PLLIDF = DSI_PLL_IN_DIV4;
dsi_pllinit.PLLODF = DSI_PLL_OUT_DIV1;
uint32_t LANE_BYTE_CLOCK = 62500;
display.hdsi.Instance = DSI;
display.hdsi.Init.NumberOfLanes = DSI_TWO_DATA_LANES;
display.hdsi.Init.TXEscapeCkdiv = 3;
display.hdsi.Init.AutomaticClockLaneControl = DSI_AUTO_CLK_LANE_CTRL_DISABLE;
HAL_DSI_Init(&display.hdsi, &dsi_pllinit);
#if OMV_DSI_DISPLAY_TE_ENABLE
DSI_CmdCfgTypeDef dsi_cmd;
dsi_cmd.VirtualChannelID = self->vcid;
dsi_cmd.HSPolarity = DSI_HSYNC_ACTIVE_LOW;
dsi_cmd.VSPolarity = DSI_VSYNC_ACTIVE_LOW;
dsi_cmd.DEPolarity = DSI_DATA_ENABLE_ACTIVE_HIGH;
dsi_cmd.ColorCoding = DSI_RGB565;
dsi_cmd.CommandSize = 0xFFFFU;
dsi_cmd.TearingEffectSource = DSI_TE_DSILINK;
dsi_cmd.TearingEffectPolarity = DSI_TE_RISING_EDGE;
dsi_cmd.VSyncPol = DSI_VSYNC_FALLING;
dsi_cmd.AutomaticRefresh = DSI_AR_DISABLE;
dsi_cmd.TEAcknowledgeRequest = DSI_TE_ACKNOWLEDGE_ENABLE;
HAL_DSI_ConfigAdaptedCommandMode(&display.hdsi, &dsi_cmd);
#endif
// Configure DSI PHY HS2LP and LP2HS timings
DSI_PHY_TimerTypeDef dsi_phyinit;
dsi_phyinit.ClockLaneHS2LPTime = 35;
dsi_phyinit.ClockLaneLP2HSTime = 35;
dsi_phyinit.DataLaneHS2LPTime = 35;
dsi_phyinit.DataLaneLP2HSTime = 35;
dsi_phyinit.DataLaneMaxReadTime = 0;
dsi_phyinit.StopWaitTime = 10;
HAL_DSI_ConfigPhyTimer(&display.hdsi, &dsi_phyinit);
HAL_DSI_ConfigFlowControl(&display.hdsi, DSI_FLOW_CONTROL_BTA);
HAL_DSI_SetLowPowerRXFilter(&display.hdsi, 10000);
HAL_DSI_ConfigErrorMonitor(&display.hdsi, HAL_DSI_ERROR_NONE);
// Timing parameters for Video modes
DSI_VidCfgTypeDef dsi_vidcfg = { 0 };
dsi_vidcfg.VirtualChannelID = self->vcid;
dsi_vidcfg.ColorCoding = DSI_RGB565;
dsi_vidcfg.LooselyPacked = DSI_LOOSELY_PACKED_DISABLE;
dsi_vidcfg.VSPolarity = DSI_VSYNC_ACTIVE_LOW;
dsi_vidcfg.HSPolarity = DSI_HSYNC_ACTIVE_LOW;
dsi_vidcfg.DEPolarity = DSI_DATA_ENABLE_ACTIVE_HIGH;
dsi_vidcfg.Mode = DSI_VID_MODE_BURST;
dsi_vidcfg.NullPacketSize = 0xFFF;
dsi_vidcfg.NumberOfChunks = 0;
dsi_vidcfg.PacketSize = self->width;
dsi_vidcfg.HorizontalSyncActive = dm->hsync_len * LANE_BYTE_CLOCK / dm->pixel_clock;
dsi_vidcfg.HorizontalBackPorch = dm->hback_porch * LANE_BYTE_CLOCK / dm->pixel_clock;
dsi_vidcfg.HorizontalLine = (self->width + dm->hsync_len + dm->hback_porch + dm->hfront_porch)
* LANE_BYTE_CLOCK / dm->pixel_clock;
dsi_vidcfg.VerticalSyncActive = dm->vsync_len;
dsi_vidcfg.VerticalBackPorch = dm->vback_porch;
dsi_vidcfg.VerticalFrontPorch = dm->vfront_porch;
dsi_vidcfg.VerticalActive = self->height;
// Enable/disable sending LP command while streaming
dsi_vidcfg.LPCommandEnable = DSI_LP_COMMAND_ENABLE;
// Largest packet size possible to transmit in LP mode in VSA, VBP, VFP regions
dsi_vidcfg.LPLargestPacketSize = 0;
// Largest packet size possible to transmit in LP mode in HFP region during VACT period
dsi_vidcfg.LPVACTLargestPacketSize = 0;
// Specify for each region, if the going in LP mode is allowed while streaming
dsi_vidcfg.LPHorizontalFrontPorchEnable = DSI_LP_HFP_ENABLE;
dsi_vidcfg.LPHorizontalBackPorchEnable = DSI_LP_HBP_ENABLE;
dsi_vidcfg.LPVerticalActiveEnable = DSI_LP_VACT_ENABLE;
dsi_vidcfg.LPVerticalFrontPorchEnable = DSI_LP_VFP_ENABLE;
dsi_vidcfg.LPVerticalBackPorchEnable = DSI_LP_VBP_ENABLE;
dsi_vidcfg.LPVerticalSyncActiveEnable = DSI_LP_VSYNC_ENABLE;
// Configure DSI Video mode timings with settings set above
HAL_DSI_ConfigVideoMode(&display.hdsi, &dsi_vidcfg);
HAL_DSI_Start(&display.hdsi);
HAL_DSI_Refresh(&display.hdsi);
HAL_DSI_PatternGeneratorStop(&display.hdsi);
HAL_NVIC_SetPriority(DSI_IRQn, IRQ_PRI_LTDC, 0);
HAL_NVIC_EnableIRQ(DSI_IRQn);
}
#endif
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);
@ -240,12 +346,12 @@ static void ltdc_init(py_display_obj_t *self) {
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.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.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;
@ -281,8 +387,8 @@ void HAL_LTDC_LineEventCallback(LTDC_HandleTypeDef *hltdc) {
}
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) {
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;
@ -412,7 +518,50 @@ static void display_set_backlight(py_display_obj_t *self, uint32_t intensity) {
}
#endif // OMV_DISPLAY_BL_PIN
#ifdef OMV_DSI_DISPLAY_CONTROLLER
int display_dsi_write(py_display_obj_t *self, uint8_t cmd, uint8_t *args, size_t n_args, bool dcs) {
HAL_StatusTypeDef status = HAL_ERROR;
if (n_args == 0) {
status = HAL_DSI_ShortWrite(&display.hdsi, self->vcid, (dcs == true) ?
DSI_DCS_SHORT_PKT_WRITE_P0 : DSI_GEN_SHORT_PKT_WRITE_P1, cmd, 0x00);
} else if (n_args == 1) {
status = HAL_DSI_ShortWrite(&display.hdsi, self->vcid, (dcs == true) ?
DSI_DCS_SHORT_PKT_WRITE_P1 : DSI_GEN_SHORT_PKT_WRITE_P2, cmd, args[0]);
} else {
status = HAL_DSI_LongWrite(&display.hdsi, self->vcid, (dcs == true) ?
DSI_DCS_LONG_PKT_WRITE : DSI_GEN_LONG_PKT_WRITE, n_args, cmd, args);
}
if (status != HAL_OK) {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("DSI write command failed"));
}
return 0;
}
int display_dsi_read(py_display_obj_t *self, uint8_t cmd, uint8_t *args,
size_t n_args, uint8_t *buf, size_t len, bool dcs) {
HAL_StatusTypeDef status = HAL_ERROR;
uint8_t params[] = { cmd, (args == NULL) ? 0 : args[0] };
if (n_args == 0) {
// For generic commands, the HAL expects cmd in ParametersTable[0U]
status = HAL_DSI_Read(&display.hdsi, self->vcid, buf, len, (dcs == true) ?
DSI_DCS_SHORT_PKT_READ : DSI_GEN_SHORT_PKT_READ_P1, cmd, params);
} else if (n_args == 1) {
status = HAL_DSI_Read(&display.hdsi, self->vcid,
buf, len, DSI_GEN_SHORT_PKT_READ_P2, 0, params);
}
if (status != HAL_OK) {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("DSI read command failed"));
}
return 0;
}
#endif
static void display_deinit(py_display_obj_t *self) {
#ifdef OMV_DSI_DISPLAY_CONTROLLER
HAL_DSI_DeInit(&display.hdsi);
HAL_NVIC_DisableIRQ(DSI_IRQn);
#endif
HAL_LTDC_DeInit(&display.hltdc);
HAL_NVIC_DisableIRQ(LTDC_IRQn);
@ -435,13 +584,15 @@ static void display_deinit(py_display_obj_t *self) {
}
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 };
enum { ARG_framesize, ARG_refresh, ARG_display_on, ARG_triple_buffer, ARG_portrait, ARG_channel, ARG_controller };
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_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} },
{ MP_QSTR_channel, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0 } },
{ MP_QSTR_controller, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
};
// Parse args.
@ -456,7 +607,12 @@ mp_obj_t display_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw,
}
py_display_obj_t *self = (py_display_obj_t *) m_new_obj_with_finaliser(py_display_obj_t);
#ifdef OMV_DSI_DISPLAY_CONTROLLER
self->base.type = &py_dsi_display_type;
#else
self->base.type = &py_rgb_display_type;
#endif
self->vcid = args[ARG_channel].u_int;
self->refresh = args[ARG_refresh].u_int;
self->display_on = args[ARG_display_on].u_bool;
self->bgr = false;
@ -471,16 +627,32 @@ mp_obj_t display_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw,
self->width = display_modes[self->framesize].hactive;
self->height = display_modes[self->framesize].vactive;
}
self->controller = args[ARG_controller].u_obj;
// 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_DSI_DISPLAY_CONTROLLER
// Init DSI controller
dsi_init(self);
// Init the display controller.
if (self->controller != mp_const_none) {
mp_obj_t dest[3];
mp_load_method_maybe(self->controller, MP_QSTR_init, dest);
if (dest[0] != MP_OBJ_NULL) {
dest[2] = MP_OBJ_FROM_PTR(self);
mp_call_method_n_kw(1, 0, dest);
}
}
#endif
#ifdef OMV_DISPLAY_BL_PIN
if (self->display_on) {
display_set_backlight(self, 255); // to on state
@ -497,8 +669,13 @@ STATIC const py_display_p_t py_display_p = {
#ifdef OMV_DISPLAY_BL_PIN
.set_backlight = display_set_backlight,
#endif
#ifdef OMV_DSI_DISPLAY_CONTROLLER
.dsi_write = display_dsi_write,
.dsi_read = display_dsi_read,
#endif
};
#ifdef OMV_RGB_DISPLAY_CONTROLLER
MP_DEFINE_CONST_OBJ_TYPE(
py_rgb_display_type,
MP_QSTR_RGBDisplay,
@ -507,4 +684,17 @@ MP_DEFINE_CONST_OBJ_TYPE(
protocol, &py_display_p,
locals_dict, &py_display_locals_dict
);
#endif
#ifdef OMV_DSI_DISPLAY_CONTROLLER
MP_DEFINE_CONST_OBJ_TYPE(
py_dsi_display_type,
MP_QSTR_DSIDisplay,
MP_TYPE_FLAG_NONE,
make_new, display_make_new,
protocol, &py_display_p,
locals_dict, &py_display_locals_dict
);
#endif
#endif // MICROPY_PY_DISPLAY

View File

@ -601,7 +601,19 @@ void HAL_JPEG_MspDeInit(JPEG_HandleTypeDef *hjpeg) {
}
#endif
#if defined(OMV_RGB_DISPLAY_CONTROLLER)
#if defined(OMV_DSI_DISPLAY_CONTROLLER)
void HAL_DSI_MspInit(DSI_HandleTypeDef *hdsi) {
__HAL_RCC_DSI_CLK_ENABLE();
}
void HAL_DSI_MspDeInit(DSI_HandleTypeDef *hdsi) {
__HAL_RCC_DSI_FORCE_RESET();
__HAL_RCC_DSI_RELEASE_RESET();
__HAL_RCC_DSI_CLK_DISABLE();
}
#endif
#if defined(OMV_RGB_DISPLAY_CONTROLLER) || defined(OMV_DSI_DISPLAY_CONTROLLER)
void HAL_LTDC_MspInit(LTDC_HandleTypeDef *hltdc) {
#if defined(OMV_RGB_DISPLAY_R0_PIN)
const omv_gpio_t ltdc_pins[] = {