diff --git a/docs/integration/driver/display/gen_mipi.rst b/docs/integration/driver/display/gen_mipi.rst new file mode 100644 index 000000000..80b4dc99d --- /dev/null +++ b/docs/integration/driver/display/gen_mipi.rst @@ -0,0 +1,212 @@ +================================================= +Generic MIPI DCS compatible LCD Controller driver +================================================= + +Overview +------------- + +From the `Wikipedia `__: + + `MIPI Allience `__ is a global business alliance that develops technical specifications + for the mobile ecosystem, particularly smart phones but including mobile-influenced industries. MIPI was founded in 2003 by Arm, Intel, Nokia, Samsung, + STMicroelectronics and Texas Instruments. + +MIPI Allience published a series of specifications related to display devices, including DBI (Display Bus Interface), DSI (Display Serial Interface) and DCS +Display Command Set). Usually when one talks about a MIPI-compatible display, one thinks of a device with a DSI serial interface. However, the DBI specification +includes a number of other, legacy interfaces, like SPI and a 8080-compatible parallel interface, which are often used to interface LCD displays to microcontrollers. +Furthermore, the DCS specification contains a standard command set, which is supported by a large number of legacy TFT LCD controllers, including the popular Sitronix +(ST7735, ST7789, ST7796) and Ilitek (ILI9341) SOCs. + +The DCS command set provides a common interface to configure display orientation, color resolution, various power modes, and provide generic video memory access. On top +of that standard command set each LCD controller chip has a number of vendor-specific commands to configure voltage generator levels, timings, or gamma curves. + +It is important to understand that this generic MIPI LCD driver is not a hardware driver for displays with DSI interface. Instead, it implements the MIPI DCS command +set, and provides a common framework for chip-specific display controllers. + +.. tip:: + Although this is a generic driver, it can be used to support compatible chips which do not have a specific driver. + + +Prerequisites +------------- + +There are no prerequisites. + +Configuring the driver +---------------------- + +Enable the generic MIPI LCD driver support in lv_conf.h, by cmake compiler define or by KConfig + +.. code:: c + + #define LV_USE_GENERIC_MIPI 1 + +.. note:: + :c:macro:`LV_USE_GENERIC_MIPI` is automatically enabled when a compatible driver is enabled. + +Usage +----- + +You need to implement two platform-dependent functions: + +.. code:: c + + /* Send short command to the LCD. This function shall wait until the transaction finishes. */ + int32_t my_lcd_send_cmd(lv_display_t *disp, const uint8_t *cmd, size_t cmd_size, const uint8_t *param, size_t param_size) + { + ... + } + + /* Send large array of pixel data to the LCD. If necessary, this function has to do the byte-swapping. This function can do the transfer in the background. */ + int32_t my_lcd_send_color(lv_display_t *disp, const uint8_t *cmd, size_t cmd_size, uint8_t *param, size_t param_size) + { + ... + } + +The only difference between the :cpp:func:`my_lcd_send_cmd()` and :cpp:func:`my_lcd_send_color()` functions is that :cpp:func:`my_lcd_send_cmd()` is used to send short commands and it is expected +complete the transaction when it returns (in other words, it should be blocking), while :cpp:func:`my_lcd_send_color()` is only used to send pixel data, and it is recommended to use +DMA to transmit data in the background. More sophisticated methods can be also implemented, like queing transfers and scheduling them in the background. + +Please note that while display flushing is handled by the driver, it is the user's responsibility to call :cpp:func:`lv_display_flush_ready()` +when the color transfer completes. In case of a DMA transfer this is usually done in a transfer ready callback. + +.. note:: + While it is acceptable to use a blocking implementation for the pixel transfer as well, performance will suffer. + +.. tip:: + Care must be taken to avoid sending a command while there is an active transfer going on in the background. It is the user's responsibility to implement this either + by polling the hardware, polling a global variable (which is reset at the end of the transfer), or by using a semaphore or other locking mechanism. + +Please also note that the driver does not handle the draw buffer allocation, because this may be platform-dependent, too. Thus you need to allocate the buffers and assign them +to the display object as usual by calling :cpp:func:`lv_display_set_draw_buffers()`. + +The driver can be used to create multiple displays. In such a configuration the callbacks must be able to distinguish between the displays. Usually one would +implement a separate set of callbacks for each display. Also note that the user must take care of arbitrating the bus when multiple devices are connected to it. + +Example +------- + +.. note:: + You can find the actual implementation of the callbacks on an STM32F746 using STM32CubeIDE and the ST HAL libraries + `here `__. + +.. code:: c + + #include "src/dev/display/st7789/lv_st7789.h" + + #define LCD_H_RES 240 + #define LCD_V_RES 320 + #define LCD_BUF_LINES 60 + + lv_display_t *my_disp; + + ... + + + /* Initialize LCD I/O bus, reset LCD */ + static int32_t my_lcd_io_init(void) + { + ... + return HAL_OK; + } + + /* Send command to the LCD controller */ + static void my_lcd_send_cmd(lv_display_t *disp, const uint8_t *cmd, size_t cmd_size, const uint8_t *param, size_t param_size) + { + ... + } + + /* Send pixel data to the LCD controller */ + static void my_lcd_send_color(lv_display_t *disp, const uint8_t *cmd, size_t cmd_size, uint8_t *param, size_t param_size) + { + ... + } + + int main(int argc, char ** argv) + { + ... + + /* Initialize LVGL */ + lv_init(); + + /* Initialize LCD bus I/O */ + if (my_lcd_io_init() != 0) + return; + + /* Create the LVGL display object and the LCD display driver */ + my_disp = lv_lcd_generic_mipi_create(LCD_H_RES, LCD_V_RES, LV_LCD_FLAG_NONE, my_lcd_send_cmd, my_lcd_send_color); + + /* Set display orientation to landscape */ + lv_display_set_rotation(my_disp, LV_DISPLAY_ROTATION_90); + + /* Configure draw buffers, etc. */ + lv_color_t * buf1 = NULL; + lv_color_t * buf2 = NULL; + + uint32_t buf_size = LCD_H_RES * LCD_BUF_LINES * lv_color_format_get_size(lv_disp_get_color_format(my_disp)); + + buf1 = lv_malloc(buf_size); + if(buf1 == NULL) { + LV_LOG_ERROR("display draw buffer malloc failed"); + return; + } + /* Allocate secondary buffer if needed */ + ... + + lv_display_set_buffers(my_disp, buf1, buf2, buf_size, LV_DISPLAY_RENDER_MODE_PARTIAL); + + ui_init(my_disp); + + while(true) { + ... + + /* Periodically call the lv_timer handler */ + lv_timer_handler(); + } + } + +Advanced topics +--------------- + +Create flags +^^^^^^^^^^^^ + +The third argument of the :cpp:func:`lv_lcd_generic_mipi_create()` function is a flag array. This can be used to configure the orientation and RGB ordering of the panel if the +default settings do not work for you. In particular, the generic MIPI driver accepts the following flags: + +.. code:: c + + LV_LCD_FLAG_NONE + LV_LCD_FLAG_MIRROR_X + LV_LCD_FLAG_MIRROR_Y + LV_LCD_FLAG_BGR + +You can pass multiple flags by ORing them together, e.g., :c:macro:`LV_LCD_FLAG_MIRROR_X | LV_LCD_FLAG_BGR`. + +Custom command lists +^^^^^^^^^^^^^^^^^^^^ + +While the chip-specific drivers do their best to initialize the LCD controller correctly, it is possible, that different TFT panels need different configurations. +In particular a correct gamma setup is crucial for good color reproduction. Unfortunately, finding a good set of parameters is not easy. Usually the manufacturer +of the panel provides some example code with recommended register settings. + +You can use the ``my_lcd_send_cmd()`` function to send an arbitrary command to the LCD controller. However, to make it easier to send a large number of parameters +the generic MIPI driver supports sending a custom command list to the controller. The commands must be put into a 'uint8_t' array: + +.. code:: c + + static const uint8_t init_cmd_list[] = { + , , , ... , + , , , ... , + ... + LV_LCD_CMD_DELAY_MS, LV_LCD_CMD_EOF /* terminate list: this is required! */ + }; + + ... + + lv_lcd_generic_mipi_send_cmd_list(my_disp, init_cmd_list); + +You can add a delay between the commands by using the pseudo-command ``LV_LCD_CMD_DELAY_MS``, which must be followed by the delay given in 10ms units. +To terminate the command list you must use a delay with a value of ``LV_LCD_CMD_EOF``, as shown above. + +See an actual example of sending a command list `here `__. diff --git a/docs/integration/driver/display/ili9341.rst b/docs/integration/driver/display/ili9341.rst index eeddf5f89..f48c4a1f1 100644 --- a/docs/integration/driver/display/ili9341.rst +++ b/docs/integration/driver/display/ili9341.rst @@ -1,5 +1,68 @@ -======= -ILI9341 -======= +============================= +ILI9341 LCD Controller driver +============================= -TODO \ No newline at end of file +Overview +------------- + +The `ILI9341 `__ is a 262,144-color single-chip SOC driver for a-TFT liquid crystal display with resolution of 240RGBx320 +dots, comprising a 720-channel source driver, a 320-channel gate driver, 172,800 bytes GRAM for graphic +display data of 240RGBx320 dots, and power supply circuit. +ILI9341 supports parallel 8-/9-/16-/18-bit data bus MCU interface, 6-/16-/18-bit data bus RGB interface and +3-/4-line serial peripheral interface (SPI). + +The ILI9341 LCD controller `driver `__ is a platform-agnostic driver, based on the `generic MIPI driver `__. +It implements display initialization, supports display rotation and implements the display flush callback. The user needs to implement only two platform-specific functions to send +a command or pixel data to the controller via SPI or parallel bus. Typically these are implemented by calling the appropriate SDK library functions on the given platform. + +Prerequisites +------------- + +There are no prerequisites. + +Configuring the driver +---------------------- + +Enable the ILI9341 driver support in lv_conf.h, by cmake compiler define or by KConfig + +.. code:: c + + #define LV_USE_ILI9341 1 + +Usage +----- + +You need to implement two platform-dependent functions: + +.. code:: c + + /* Send short command to the LCD. This function shall wait until the transaction finishes. */ + int32_t my_lcd_send_cmd(lv_display_t *disp, const uint8_t *cmd, size_t cmd_size, const uint8_t *param, size_t param_size) + { + ... + } + + /* Send large array of pixel data to the LCD. If necessary, this function has to do the byte-swapping. This function can do the transfer in the background. */ + int32_t my_lcd_send_color(lv_display_t *disp, const uint8_t *cmd, size_t cmd_size, uint8_t *param, size_t param_size) + { + ... + } + +To create an ILI9341-based display use the function + +.. code:: c + + /** + * Create an LCD display with ILI9341 driver + * @param hor_res horizontal resolution + * @param ver_res vertical resolution + * @param flags default configuration settings (mirror, RGB ordering, etc.) + * @param send_cmd platform-dependent function to send a command to the LCD controller (usually uses polling transfer) + * @param send_color platform-dependent function to send pixel data to the LCD controller (usually uses DMA transfer: must implement a 'ready' callback) + * @return pointer to the created display + */ + lv_display_t * lv_ili9341_create(uint32_t hor_res, uint32_t ver_res, lv_lcd_flag_t flags, + lv_ili9341_send_cmd_cb_t send_cmd_cb, lv_ili9341_send_color_cb_t send_color_cb); + + +For additional details and a working example see the `generic MIPI driver documentation `__. diff --git a/docs/integration/driver/display/index.rst b/docs/integration/driver/display/index.rst index bc7ae06af..04c0d2986 100644 --- a/docs/integration/driver/display/index.rst +++ b/docs/integration/driver/display/index.rst @@ -6,3 +6,6 @@ Display :maxdepth: 2 ili9341 + st7735 + st7789 + st7796 diff --git a/docs/integration/driver/display/lcd_stm32_hal.rst b/docs/integration/driver/display/lcd_stm32_hal.rst new file mode 100644 index 000000000..6a748df40 --- /dev/null +++ b/docs/integration/driver/display/lcd_stm32_hal.rst @@ -0,0 +1,109 @@ +================================================================================================== +LCD driver SPI bus I/O implementation example for STM32 devices using the STM32Cube HAL SPI driver +================================================================================================== + +This is an example implementation of an SPI bus driver using STM32CubeIDE and the ST HAL libraries, tested on a Nucleo-F746ZG board. This code example only implements +the LCD driver specific parts, so you still have to configure the hardware using STM32CubeMX or STM32CubeIDE. It is not meant as the best possible implementation, +but since it uses DMA for the pixel transfer, it has a good performance. + +To use this code without change you need to name the appropriate GPIO pins as follows: + +.. code:: c + + LCD_RESET /* Reset */ + LCD_CS /* Chip Select */ + LCD_DCX /* Data/Command Select */ + +The example code uses the SPI1 port. The SPI controller of the STM32F746 is capable of 16-bit transfers, and it can swap the 16-bit pixel data bytes on the fly, so +there is no need to do this in software. This improves the performance considerably. + +This code implements a rather simple locking mechanism using a global variable :cpp:var:`my_disp_bus_busy` to prevent accessing the controller while there is a DMA transfer +going on in the background. In a more sophisticated implementation this could be replaced with a semaphore or a transaction queue. + +.. code:: c + + #include "stm32f7xx_hal.h" + #include "lvgl.h" + + ... + + lv_display_t *my_disp; + volatile int my_disp_bus_busy = 0; + + ... + + /* DMA transfer ready callback */ + static void my_lcd_color_transfer_ready_cb(SPI_HandleTypeDef *hspi) + { + /* CS high */ + HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET); + my_disp_bus_busy = 0; + lv_display_flush_ready(my_disp); + } + + /* Initialize LCD I/O bus, reset LCD */ + static int32_t my_lcd_io_init(void) + { + /* Register SPI Tx Complete Callback */ + HAL_SPI_RegisterCallback(&hspi1, HAL_SPI_TX_COMPLETE_CB_ID, stm32_lcd_color_transfer_ready_cb); + + /* reset LCD */ + HAL_GPIO_WritePin(LCD_RESET_GPIO_Port, LCD_RESET_Pin, GPIO_PIN_RESET); + HAL_Delay(100); + HAL_GPIO_WritePin(LCD_RESET_GPIO_Port, LCD_RESET_Pin, GPIO_PIN_SET); + HAL_Delay(100); + + HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET); + HAL_GPIO_WritePin(LCD_DCX_GPIO_Port, LCD_DCX_Pin, GPIO_PIN_SET); + + return HAL_OK; + } + + /* Send short command to the LCD. This function shall wait until the transaction finishes. */ + static void my_lcd_send_cmd(lv_display_t *disp, const uint8_t *cmd, size_t cmd_size, const uint8_t *param, size_t param_size) + { + LV_UNUSED(disp); + /* Set the SPI in 8-bit mode */ + hspi1.Init.DataSize = SPI_DATASIZE_8BIT; + HAL_SPI_Init(&hspi1); + /* DCX low (command) */ + HAL_GPIO_WritePin(LCD_DCX_GPIO_Port, LCD_DCX_Pin, GPIO_PIN_RESET); + /* CS low */ + HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET); + /* send command */ + if (HAL_SPI_Transmit(&hspi1, cmd, cmd_size, BUS_SPI1_POLL_TIMEOUT) == HAL_OK) { + /* DCX high (data) */ + HAL_GPIO_WritePin(LCD_DCX_GPIO_Port, LCD_DCX_Pin, GPIO_PIN_SET); + /* for short data blocks we use polling transfer */ + HAL_SPI_Transmit(&hspi1, (uint8_t *)param, (uint16_t)param_size, BUS_SPI1_POLL_TIMEOUT); + /* CS high */ + HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET); + } + } + + /* Send large array of pixel data to the LCD. If necessary, this function has to do the byte-swapping. This function can do the transfer in the background. */ + static void my_lcd_send_color(lv_display_t *disp, const uint8_t *cmd, size_t cmd_size, uint8_t *param, size_t param_size) + { + LV_UNUSED(disp); + while (my_disp_bus_busy); /* wait until previous transfer is finished */ + /* Set the SPI in 8-bit mode */ + hspi1.Init.DataSize = SPI_DATASIZE_8BIT; + HAL_SPI_Init(&hspi1); + /* DCX low (command) */ + HAL_GPIO_WritePin(LCD_DCX_GPIO_Port, LCD_DCX_Pin, GPIO_PIN_RESET); + /* CS low */ + HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET); + /* send command */ + if (HAL_SPI_Transmit(&hspi1, cmd, cmd_size, BUS_SPI1_POLL_TIMEOUT) == HAL_OK) { + /* DCX high (data) */ + HAL_GPIO_WritePin(LCD_DCX_GPIO_Port, LCD_DCX_Pin, GPIO_PIN_SET); + /* for color data use DMA transfer */ + /* Set the SPI in 16-bit mode to match endianess */ + hspi1.Init.DataSize = SPI_DATASIZE_16BIT; + HAL_SPI_Init(&hspi1); + my_disp_bus_busy = 1; + HAL_SPI_Transmit_DMA(&hspi1, param, (uint16_t)param_size / 2); + + /* NOTE: CS will be reset in the transfer ready callback */ + } + } diff --git a/docs/integration/driver/display/st7735.rst b/docs/integration/driver/display/st7735.rst new file mode 100644 index 000000000..ea7bf54ea --- /dev/null +++ b/docs/integration/driver/display/st7735.rst @@ -0,0 +1,70 @@ +============================= +ST7735 LCD Controller driver +============================= + +Overview +------------- + +The `ST7735S `__ is a single-chip controller/driver for 262K-color, graphic type TFT-LCD. It consists of 396 +source line and 162 gate line driving circuits. This chip is capable of connecting directly to an external +microprocessor, and accepts Serial Peripheral Interface (SPI), 8-bit/9-bit/16-bit/18-bit parallel interface. +Display data can be stored in the on-chip display data RAM of 132 x 162 x 18 bits. It can perform display data +RAM read/write operation with no external operation clock to minimize power consumption. In addition, +because of the integrated power supply circuits necessary to drive liquid crystal, it is possible to make a +display system with fewer components. + +The ST7735 LCD controller `driver `__ is a platform-agnostic driver, based on the `generic MIPI driver `__. +It implements display initialization, supports display rotation and implements the display flush callback. The user needs to implement only two platform-specific functions to send +a command or pixel data to the controller via SPI or parallel bus. Typically these are implemented by calling the appropriate SDK library functions on the given platform. + +Prerequisites +------------- + +There are no prerequisites. + +Configuring the driver +---------------------- + +Enable the ST7735 driver support in lv_conf.h, by cmake compiler define or by KConfig + +.. code:: c + + #define LV_USE_ST7735 1 + +Usage +----- + +You need to implement two platform-dependent functions: + +.. code:: c + + /* Send short command to the LCD. This function shall wait until the transaction finishes. */ + int32_t my_lcd_send_cmd(lv_display_t *disp, const uint8_t *cmd, size_t cmd_size, const uint8_t *param, size_t param_size) + { + ... + } + + /* Send large array of pixel data to the LCD. If necessary, this function has to do the byte-swapping. This function can do the transfer in the background. */ + int32_t my_lcd_send_color(lv_display_t *disp, const uint8_t *cmd, size_t cmd_size, uint8_t *param, size_t param_size) + { + ... + } + +To create an ST7735-based display use the function + +.. code:: c + + /** + * Create an LCD display with ST7735 driver + * @param hor_res horizontal resolution + * @param ver_res vertical resolution + * @param flags default configuration settings (mirror, RGB ordering, etc.) + * @param send_cmd platform-dependent function to send a command to the LCD controller (usually uses polling transfer) + * @param send_color platform-dependent function to send pixel data to the LCD controller (usually uses DMA transfer: must implement a 'ready' callback) + * @return pointer to the created display + */ + lv_display_t * lv_st7735_create(uint32_t hor_res, uint32_t ver_res, lv_lcd_flag_t flags, + lv_st7735_send_cmd_cb_t send_cmd_cb, lv_st7735_send_color_cb_t send_color_cb); + + +For additional details and a working example see the `generic MIPI driver documentation `__. diff --git a/docs/integration/driver/display/st7789.rst b/docs/integration/driver/display/st7789.rst new file mode 100644 index 000000000..2bbb847ac --- /dev/null +++ b/docs/integration/driver/display/st7789.rst @@ -0,0 +1,69 @@ +============================= +ST7789 LCD Controller driver +============================= + +Overview +------------- + +The `ST7789 `__ is a single-chip controller/driver for 262K-color, graphic type TFT-LCD. It consists of 720 +source line and 320 gate line driving circuits. This chip is capable of connecting directly to an external +microprocessor, and accepts, 8-bits/9-bits/16-bits/18-bits parallel interface. Display data can be stored in the +on-chip display data RAM of 240x320x18 bits. It can perform display data RAM read/write operation with no +external operation clock to minimize power consumption. In addition, because of the integrated power supply +circuit necessary to drive liquid crystal; it is possible to make a display system with the fewest components. + +The ST7789 LCD controller `driver `__ is a platform-agnostic driver, based on the `generic MIPI driver `__. +It implements display initialization, supports display rotation and implements the display flush callback. The user needs to implement only two platform-specific functions to send +a command or pixel data to the controller via SPI or parallel bus. Typically these are implemented by calling the appropriate SDK library functions on the given platform. + +Prerequisites +------------- + +There are no prerequisites. + +Configuring the driver +---------------------- + +Enable the ST7789 driver support in lv_conf.h, by cmake compiler define or by KConfig + +.. code:: c + + #define LV_USE_ST7789 1 + +Usage +----- + +You need to implement two platform-dependent functions: + +.. code:: c + + /* Send short command to the LCD. This function shall wait until the transaction finishes. */ + int32_t my_lcd_send_cmd(lv_display_t *disp, const uint8_t *cmd, size_t cmd_size, const uint8_t *param, size_t param_size) + { + ... + } + + /* Send large array of pixel data to the LCD. If necessary, this function has to do the byte-swapping. This function can do the transfer in the background. */ + int32_t my_lcd_send_color(lv_display_t *disp, const uint8_t *cmd, size_t cmd_size, uint8_t *param, size_t param_size) + { + ... + } + +To create an ST7789-based display use the function + +.. code:: c + + /** + * Create an LCD display with ST7789 driver + * @param hor_res horizontal resolution + * @param ver_res vertical resolution + * @param flags default configuration settings (mirror, RGB ordering, etc.) + * @param send_cmd platform-dependent function to send a command to the LCD controller (usually uses polling transfer) + * @param send_color platform-dependent function to send pixel data to the LCD controller (usually uses DMA transfer: must implement a 'ready' callback) + * @return pointer to the created display + */ + lv_display_t * lv_st7789_create(uint32_t hor_res, uint32_t ver_res, lv_lcd_flag_t flags, + lv_st7789_send_cmd_cb_t send_cmd_cb, lv_st7789_send_color_cb_t send_color_cb); + + +For additional details and a working example see the `generic MIPI driver documentation `__. diff --git a/docs/integration/driver/display/st7796.rst b/docs/integration/driver/display/st7796.rst new file mode 100644 index 000000000..27fe92d08 --- /dev/null +++ b/docs/integration/driver/display/st7796.rst @@ -0,0 +1,70 @@ +============================= +ST7796 LCD Controller driver +============================= + +Overview +------------- + +The `ST7796S `__ is a single-chip controller/driver for 262K-color, graphic type TFT-LCD. It consists of 960 +source lines and 480 gate lines driving circuits. The ST7796S is capable of connecting directly to an external +microprocessor, and accepts 8-bit/9-bit/16-bit/18-bit parallel interface, SPI, and the ST7796S also provides +MIPI interface. Display data can be stored in the on-chip display data RAM of 320x480x18 bits. It can perform +display data RAM read-/write-operation with no external clock to minimize power consumption. In addition, +because of the integrated power supply circuit necessary to drive liquid crystal; it is possible to make a display +system with fewest components. + +The ST7796 LCD controller `driver `__ is a platform-agnostic driver, based on the `generic MIPI driver `__. +It implements display initialization, supports display rotation and implements the display flush callback. The user needs to implement only two platform-specific functions to send +a command or pixel data to the controller via SPI or parallel bus. Typically these are implemented by calling the appropriate SDK library functions on the given platform. + +Prerequisites +------------- + +There are no prerequisites. + +Configuring the driver +---------------------- + +Enable the ST7796 driver support in lv_conf.h, by cmake compiler define or by KConfig + +.. code:: c + + #define LV_USE_ST7796 1 + +Usage +----- + +You need to implement two platform-dependent functions: + +.. code:: c + + /* Send short command to the LCD. This function shall wait until the transaction finishes. */ + int32_t my_lcd_send_cmd(lv_display_t *disp, const uint8_t *cmd, size_t cmd_size, const uint8_t *param, size_t param_size) + { + ... + } + + /* Send large array of pixel data to the LCD. If necessary, this function has to do the byte-swapping. This function can do the transfer in the background. */ + int32_t my_lcd_send_color(lv_display_t *disp, const uint8_t *cmd, size_t cmd_size, uint8_t *param, size_t param_size) + { + ... + } + +To create an ST7796-based display use the function + +.. code:: c + + /** + * Create an LCD display with ST7796 driver + * @param hor_res horizontal resolution + * @param ver_res vertical resolution + * @param flags default configuration settings (mirror, RGB ordering, etc.) + * @param send_cmd platform-dependent function to send a command to the LCD controller (usually uses polling transfer) + * @param send_color platform-dependent function to send pixel data to the LCD controller (usually uses DMA transfer: must implement a 'ready' callback) + * @return pointer to the created display + */ + lv_display_t * lv_st7796_create(uint32_t hor_res, uint32_t ver_res, lv_lcd_flag_t flags, + lv_st7796_send_cmd_cb_t send_cmd_cb, lv_st7796_send_color_cb_t send_color_cb); + + +For additional details and a working example see the `generic MIPI driver documentation `__. diff --git a/lv_conf_template.h b/lv_conf_template.h index f3cd3d0bc..d1bcd996b 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -856,6 +856,15 @@ /*Driver for evdev input devices*/ #define LV_USE_EVDEV 0 +/*Drivers for LCD devices connected via SPI/parallel port*/ +#define LV_USE_ST7735 0 +#define LV_USE_ST7789 0 +#define LV_USE_ST7796 0 +#define LV_USE_ILI9341 0 + +#define LV_USE_GENERIC_MIPI (LV_USE_ST7735 | LV_USE_ST7789 | LV_USE_ST7796 | LV_USE_ILI9341) + + /*================== * EXAMPLES *==================*/ diff --git a/src/dev/display/ili9341/lv_ili9341.c b/src/dev/display/ili9341/lv_ili9341.c new file mode 100644 index 000000000..a6b2acf7e --- /dev/null +++ b/src/dev/display/ili9341/lv_ili9341.c @@ -0,0 +1,117 @@ +/** + * @file lv_ili9341.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_ili9341.h" + +#if LV_USE_ILI9341 + +/********************* + * DEFINES + *********************/ + +#define CMD_FRMCTR1 0xB1 /* Frame Rate Control (In Normal Mode/Full Colors) */ +#define CMD_FRMCTR2 0xB2 /* Frame Rate Control (In Idle Mode/8 colors) */ +#define CMD_FRMCTR3 0xB3 /* Frame Rate control (In Partial Mode/Full Colors) */ +#define CMD_INVCTR 0xB4 /* Display Inversion Control */ +#define CMD_DFUNCTR 0xB6 /* Display Function Control */ +#define CMD_PWCTR1 0xC0 /* Power Control 1 */ +#define CMD_PWCTR2 0xC1 /* Power Control 2 */ +#define CMD_VMCTR1 0xC5 /* VCOM Control 1 */ +#define CMD_VMCTR2 0xC7 /* VCOM Control 2 */ +#define CMD_PWCTRA 0xCB /* Power Control A */ +#define CMD_PWCTRB 0xCF /* Power Control B */ +#define CMD_GMCTRP1 0xE0 /* Positive Gamma Correction */ +#define CMD_GMCTRN1 0xE1 /* Negative Gamma Correction */ +#define CMD_DTCTRA 0xE8 /* Driver timing control A */ +#define CMD_DTCTRB 0xEA /* Driver timing control B */ +#define CMD_PONSEQ 0xED /* Power On Sequence */ +#define CMD_RDINDEX 0xD9 /* ili9341 */ +#define CMD_IDXRD 0xDD /* ILI9341 only, indexed control register read */ +#define CMD_ENA3G 0xF2 /* Enable 3 Gamma control */ +#define CMD_IFCTR 0xF6 /* Interface Control */ +#define CMD_PRCTR 0xF7 /* Pump ratio control */ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC CONSTANTS + **********************/ + +/* init commands based on LovyanGFX ILI9341 driver */ +static const uint8_t init_cmd_list[] = { + CMD_PWCTRB, 3, 0x00, 0xC1, 0x30, + CMD_PONSEQ, 4, 0x64, 0x03, 0x12, 0x81, + CMD_DTCTRA, 3, 0x85, 0x00, 0x78, + CMD_PWCTRA, 5, 0x39, 0x2C, 0x00, 0x34, 0x02, + CMD_PRCTR, 1, 0x20, + CMD_DTCTRB, 2, 0x00, 0x00, + CMD_PWCTR1, 1, 0x23, + CMD_PWCTR2, 1, 0x10, + CMD_VMCTR1, 2, 0x3e, 0x28, + CMD_VMCTR2, 1, 0x86, + CMD_FRMCTR1, 2, 0x00, 0x13, + CMD_DFUNCTR, 2, 0x0A, 0xA2, + CMD_IFCTR, 3, 0x09, 0x30, 0x00, + CMD_ENA3G, 1, 0x00, + LV_LCD_CMD_SET_GAMMA_CURVE, 1, 0x01, + CMD_GMCTRP1, 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00, + CMD_GMCTRN1, 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, + LV_LCD_CMD_DELAY_MS, LV_LCD_CMD_EOF +}; + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_display_t * lv_ili9341_create(uint32_t hor_res, uint32_t ver_res, lv_lcd_flag_t flags, + lv_ili9341_send_cmd_cb_t send_cmd_cb, lv_ili9341_send_color_cb_t send_color_cb) +{ + lv_display_t * disp = lv_lcd_generic_mipi_create(hor_res, ver_res, flags, send_cmd_cb, send_color_cb); + lv_lcd_generic_mipi_send_cmd_list(disp, init_cmd_list); + return disp; +} + +void lv_ili9341_set_gap(lv_display_t * disp, uint16_t x, uint16_t y) +{ + lv_lcd_generic_mipi_set_gap(disp, x, y); +} + +void lv_ili9341_set_invert(lv_display_t * disp, bool invert) +{ + lv_lcd_generic_mipi_set_invert(disp, invert); +} + +void lv_ili9341_set_gamma_curve(lv_display_t * disp, uint8_t gamma) +{ + lv_lcd_generic_mipi_set_gamma_curve(disp, gamma); +} + +void lv_ili9341_send_cmd_list(lv_display_t * disp, const uint8_t * cmd_list) +{ + lv_lcd_generic_mipi_send_cmd_list(disp, cmd_list); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_ILI9341*/ diff --git a/src/dev/display/ili9341/lv_ili9341.h b/src/dev/display/ili9341/lv_ili9341.h new file mode 100644 index 000000000..c93fcba03 --- /dev/null +++ b/src/dev/display/ili9341/lv_ili9341.h @@ -0,0 +1,93 @@ +/* + * lv_ili9341.h + * + * This driver is just a wrapper around the generic MIPI compatible LCD controller driver + * + */ + +#ifndef LV_ILI9341_H +#define LV_ILI9341_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../lcd/lv_lcd_generic_mipi.h" + +#if LV_USE_ILI9341 + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef lv_lcd_send_cmd_cb_t lv_ili9341_send_cmd_cb_t; +typedef lv_lcd_send_color_cb_t lv_ili9341_send_color_cb_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create an LCD display with ILI9341 driver + * @param hor_res horizontal resolution + * @param ver_res vertical resolution + * @param flags default configuration settings (mirror, RGB ordering, etc.) + * @param send_cmd platform-dependent function to send a command to the LCD controller (usually uses polling transfer) + * @param send_color platform-dependent function to send pixel data to the LCD controller (usually uses DMA transfer: must implement a 'ready' callback) + * @return pointer to the created display + */ +lv_display_t * lv_ili9341_create(uint32_t hor_res, uint32_t ver_res, lv_lcd_flag_t flags, + lv_ili9341_send_cmd_cb_t send_cmd_cb, lv_ili9341_send_color_cb_t send_color_cb); + +/** + * Set gap, i.e., the offset of the (0,0) pixel in the VRAM + * @param disp display object + * @param x x offset + * @param y y offset + */ +void lv_ili9341_set_gap(lv_display_t * disp, uint16_t x, uint16_t y); + +/** + * Set color inversion + * @param disp display object + * @param invert false: normal, true: invert + */ +void lv_ili9341_set_invert(lv_display_t * disp, bool invert); + +/** + * Set gamma curve + * @param disp display object + * @param gamma gamma curve + */ +void lv_ili9341_set_gamma_curve(lv_display_t * disp, uint8_t gamma); + +/** + * Send list of commands. + * @param disp display object + * @param cmd_list controller and panel-specific commands + */ +void lv_ili9341_send_cmd_list(lv_display_t * disp, const uint8_t * cmd_list); + +/********************** + * OTHERS + **********************/ + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_USE_ILI9341*/ + +#endif /* LV_ILI9341_H */ diff --git a/src/dev/display/lcd/lv_lcd_generic_mipi.c b/src/dev/display/lcd/lv_lcd_generic_mipi.c new file mode 100644 index 000000000..ae3cf7130 --- /dev/null +++ b/src/dev/display/lcd/lv_lcd_generic_mipi.c @@ -0,0 +1,340 @@ +/** + * @file lv_lcd_generic_mipi.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_lcd_generic_mipi.h" + +#if LV_USE_GENERIC_MIPI + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void send_cmd(lv_lcd_generic_mipi_driver_t * drv, uint8_t cmd, uint8_t * param, size_t param_size); +static void send_color(lv_lcd_generic_mipi_driver_t * drv, uint8_t cmd, uint8_t * param, size_t param_size); +static void init(lv_lcd_generic_mipi_driver_t * drv, lv_lcd_flag_t flags); +static void set_mirror(lv_lcd_generic_mipi_driver_t * drv, bool mirror_x, bool mirror_y); +static void set_swap_xy(lv_lcd_generic_mipi_driver_t * drv, bool swap); +static void set_rotation(lv_lcd_generic_mipi_driver_t * drv, lv_display_rotation_t rot); +static void res_chg_event_cb(lv_event_t * e); +static lv_lcd_generic_mipi_driver_t * get_driver(lv_display_t * disp); +static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_display_t * lv_lcd_generic_mipi_create(uint32_t hor_res, uint32_t ver_res, lv_lcd_flag_t flags, + lv_lcd_send_cmd_cb_t send_cmd_cb, lv_lcd_send_color_cb_t send_color_cb) +{ + lv_display_t * disp = lv_display_create(hor_res, ver_res); + if(disp == NULL) { + return NULL; + } + + lv_lcd_generic_mipi_driver_t * drv = (lv_lcd_generic_mipi_driver_t *)lv_malloc(sizeof(lv_lcd_generic_mipi_driver_t)); + if(drv == NULL) { + lv_display_delete(disp); + return NULL; + } + + /* init driver struct */ + drv->disp = disp; + drv->send_cmd = send_cmd_cb; + drv->send_color = send_color_cb; + lv_display_set_driver_data(disp, (void *)drv); + + /* init controller */ + init(drv, flags); + + /* register resolution change callback (NOTE: this handles screen rotation as well) */ + lv_display_add_event_cb(disp, res_chg_event_cb, LV_EVENT_RESOLUTION_CHANGED, NULL); + + /* register flush callback */ + lv_display_set_flush_cb(disp, flush_cb); + + return disp; +} + +void lv_lcd_generic_mipi_set_gap(lv_display_t * disp, uint16_t x, uint16_t y) +{ + lv_lcd_generic_mipi_driver_t * drv = get_driver(disp); + drv->x_gap = x; + drv->y_gap = y; +} + +void lv_lcd_generic_mipi_set_invert(lv_display_t * disp, bool invert) +{ + lv_lcd_generic_mipi_driver_t * drv = get_driver(disp); + send_cmd(drv, invert ? LV_LCD_CMD_ENTER_INVERT_MODE : LV_LCD_CMD_EXIT_INVERT_MODE, NULL, 0); +} + +void lv_lcd_generic_mipi_set_address_mode(lv_display_t * disp, bool mirror_x, bool mirror_y, bool swap_xy, bool bgr) +{ + lv_lcd_generic_mipi_driver_t * drv = get_driver(disp); + uint8_t mad = drv->madctl_reg & ~(LV_LCD_MASK_RGB_ORDER); + if(bgr) { + mad |= LV_LCD_BIT_RGB_ORDER__BGR; + } + drv->madctl_reg = mad; + drv->mirror_x = mirror_x; + drv->mirror_y = mirror_y; + drv->swap_xy = swap_xy; + set_rotation(drv, lv_display_get_rotation(disp)); /* update screen */ +} + +void lv_lcd_generic_mipi_set_gamma_curve(lv_display_t * disp, uint8_t gamma) +{ + lv_lcd_generic_mipi_driver_t * drv = get_driver(disp); + send_cmd(drv, LV_LCD_CMD_SET_GAMMA_CURVE, (uint8_t[]) { + gamma, + }, 1); +} + +void lv_lcd_generic_mipi_send_cmd_list(lv_display_t * disp, const uint8_t * cmd_list) +{ + lv_lcd_generic_mipi_driver_t * drv = get_driver(disp); + while(1) { + uint8_t cmd = *cmd_list++; + uint8_t num = *cmd_list++; + if(cmd == LV_LCD_CMD_DELAY_MS) { + if(num == LV_LCD_CMD_EOF) /* end of list */ + break; + else { /* delay in 10 ms units*/ + lv_delay_ms((uint32_t)(num) * 10); + } + } + else { + drv->send_cmd(drv->disp, &cmd, 1, cmd_list, num); + cmd_list += num; + } + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Helper function to call the user-supplied 'send_cmd' function + * @param drv LCD driver object + * @param cmd command byte + * @param param parameter buffer + * @param param_size number of bytes of the parameters + */ +static void send_cmd(lv_lcd_generic_mipi_driver_t * drv, uint8_t cmd, uint8_t * param, size_t param_size) +{ + uint8_t cmdbuf = cmd; /* MIPI uses 8 bit commands */ + drv->send_cmd(drv->disp, &cmdbuf, 1, param, param_size); +} + +/** + * Helper function to call the user-supplied 'send_color' function + * @param drv LCD driver object + * @param cmd command byte + * @param param parameter buffer + * @param param_size number of bytes of the parameters + */ +static void send_color(lv_lcd_generic_mipi_driver_t * drv, uint8_t cmd, uint8_t * param, size_t param_size) +{ + uint8_t cmdbuf = cmd; /* MIPI uses 8 bit commands */ + drv->send_color(drv->disp, &cmdbuf, 1, param, param_size); +} + +/** + * Initialize LCD driver after a hard reset + * @param drv LCD driver object + */ +static void init(lv_lcd_generic_mipi_driver_t * drv, lv_lcd_flag_t flags) +{ + drv->x_gap = 0; + drv->y_gap = 0; + + /* init color mode and RGB order */ + drv->madctl_reg = flags & LV_LCD_FLAG_BGR ? LV_LCD_BIT_RGB_ORDER__BGR : LV_LCD_BIT_RGB_ORDER__RGB; + drv->colmod_reg = flags & LV_LCD_FLAG_RGB666 ? LV_LCD_PIXEL_FORMAT_RGB666 : LV_LCD_PIXEL_FORMAT_RGB565; + + /* init orientation */ + drv->mirror_x = flags & LV_LCD_FLAG_MIRROR_X; + drv->mirror_y = flags & LV_LCD_FLAG_MIRROR_Y; + drv->swap_xy = false; + /* update madctl_reg */ + set_swap_xy(drv, drv->swap_xy); + set_mirror(drv, drv->mirror_x, drv->mirror_y); + + /* enter sleep mode first */ + send_cmd(drv, LV_LCD_CMD_ENTER_SLEEP_MODE, NULL, 0); + lv_delay_ms(10); + + /* perform software reset */ + send_cmd(drv, LV_LCD_CMD_SOFT_RESET, NULL, 0); + lv_delay_ms(200); + + /* LCD goes into sleep mode and display will be turned off after power on reset, exit sleep mode first */ + send_cmd(drv, LV_LCD_CMD_EXIT_SLEEP_MODE, NULL, 0); + lv_delay_ms(300); + + send_cmd(drv, LV_LCD_CMD_ENTER_NORMAL_MODE, NULL, 0); + + send_cmd(drv, LV_LCD_CMD_SET_ADDRESS_MODE, (uint8_t[]) { + drv->madctl_reg, + }, 1); + send_cmd(drv, LV_LCD_CMD_SET_PIXEL_FORMAT, (uint8_t[]) { + drv->colmod_reg, + }, 1); + send_cmd(drv, LV_LCD_CMD_SET_DISPLAY_ON, NULL, 0); +} + +/** + * Set readout directions (used for rotating the display) + * @param drv LCD driver object + * @param mirror_x false: normal, true: mirrored + * @param mirror_y false: normal, true: mirrored + */ +static void set_mirror(lv_lcd_generic_mipi_driver_t * drv, bool mirror_x, bool mirror_y) +{ + uint8_t mad = drv->madctl_reg & ~(LV_LCD_MASK_COLUMN_ADDRESS_ORDER | LV_LCD_MASK_PAGE_ADDRESS_ORDER); + if(mirror_x) { + mad |= LV_LCD_BIT_COLUMN_ADDRESS_ORDER__RTOL; + } + if(mirror_y) { + mad |= LV_LCD_BIT_PAGE_ADDRESS_ORDER__BTOT; + } + drv->madctl_reg = mad; +} + +/** + * Swap horizontal and vertical readout (used for rotating the display) + * @param drv LCD driver object + * @param swap false: normal, true: swapped + */ +static void set_swap_xy(lv_lcd_generic_mipi_driver_t * drv, bool swap) +{ + uint8_t mad = drv->madctl_reg & ~(LV_LCD_MASK_PAGE_COLUMN_ORDER); + if(swap) { + mad |= LV_LCD_BIT_PAGE_COLUMN_ORDER__REVERSE; + } + drv->madctl_reg = mad; +} + +/** + * Flush display buffer to the LCD + * @param disp display object + * @param hor_res horizontal resolution + * @param area area stored in the buffer + * @param px_map buffer containing pixel data + * @note transfers pixel data to the LCD controller using the callbacks 'send_cmd' and 'send_color', which were + * passed to the 'lv_st7789_create()' function + */ +static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map) +{ + lv_lcd_generic_mipi_driver_t * drv = get_driver(disp); + + int32_t x_start = area->x1; + int32_t x_end = area->x2 + 1; + int32_t y_start = area->y1; + int32_t y_end = area->y2 + 1; + + LV_ASSERT((x_start < x_end) && (y_start < y_end) && "start position must be smaller than end position"); + + x_start += drv->x_gap; + x_end += drv->x_gap; + y_start += drv->y_gap; + y_end += drv->y_gap; + + /* define an area of frame memory where MCU can access */ + send_cmd(drv, LV_LCD_CMD_SET_COLUMN_ADDRESS, (uint8_t[]) { + (x_start >> 8) & 0xFF, + x_start & 0xFF, + ((x_end - 1) >> 8) & 0xFF, + (x_end - 1) & 0xFF, + }, 4); + send_cmd(drv, LV_LCD_CMD_SET_PAGE_ADDRESS, (uint8_t[]) { + (y_start >> 8) & 0xFF, + y_start & 0xFF, + ((y_end - 1) >> 8) & 0xFF, + (y_end - 1) & 0xFF, + }, 4); + /* transfer frame buffer */ + size_t len = (x_end - x_start) * (y_end - y_start) * lv_color_format_get_size(lv_display_get_color_format(disp)); + send_color(drv, LV_LCD_CMD_WRITE_MEMORY_START, px_map, len); +} + +/** + * Set rotation taking into account the current mirror and swap settings + * @param drv LCD driver object + * @param rot rotation + */ +static void set_rotation(lv_lcd_generic_mipi_driver_t * drv, lv_display_rotation_t rot) +{ + switch(rot) { + case LV_DISPLAY_ROTATION_0: + set_swap_xy(drv, drv->swap_xy); + set_mirror(drv, drv->mirror_x, drv->mirror_y); + break; + case LV_DISPLAY_ROTATION_90: + set_swap_xy(drv, !drv->swap_xy); + set_mirror(drv, !drv->mirror_x, drv->mirror_y); + break; + case LV_DISPLAY_ROTATION_180: + set_swap_xy(drv, drv->swap_xy); + set_mirror(drv, !drv->mirror_x, !drv->mirror_y); + break; + case LV_DISPLAY_ROTATION_270: + set_swap_xy(drv, !drv->swap_xy); + set_mirror(drv, drv->mirror_x, !drv->mirror_y); + break; + } + send_cmd(drv, LV_LCD_CMD_SET_ADDRESS_MODE, (uint8_t[]) { + drv->madctl_reg + }, 1); +} + +/** + * Handle LV_EVENT_RESOLUTION_CHANGED event (handles both resolution and rotation change) + * @param e LV_EVENT_RESOLUTION_CHANGED event + */ +static void res_chg_event_cb(lv_event_t * e) +{ + lv_display_t * disp = lv_event_get_target(e); + lv_lcd_generic_mipi_driver_t * drv = get_driver(disp); + + uint16_t hor_res = lv_display_get_horizontal_resolution(disp); + uint16_t ver_res = lv_display_get_vertical_resolution(disp); + lv_display_rotation_t rot = lv_display_get_rotation(disp); + + /* TODO: implement resolution change */ + LV_UNUSED(hor_res); + LV_UNUSED(ver_res); + + /* handle rotation */ + set_rotation(drv, rot); +} + +static lv_lcd_generic_mipi_driver_t * get_driver(lv_display_t * disp) +{ + return (lv_lcd_generic_mipi_driver_t *)lv_display_get_driver_data(disp); +} + +#endif /*LV_USE_GENERIC_MIPI*/ diff --git a/src/dev/display/lcd/lv_lcd_generic_mipi.h b/src/dev/display/lcd/lv_lcd_generic_mipi.h new file mode 100644 index 000000000..0ffe29bcf --- /dev/null +++ b/src/dev/display/lcd/lv_lcd_generic_mipi.h @@ -0,0 +1,241 @@ +/* + * lv_lcd_generic_mipi.h + * + * Generic driver for controllers adhering to the MIPI DBI/DCS specification + * + * Works with: + * + * ST7735 + * ST7789 + * ST7796 + * ILI9341 + * ILI9488 (NOTE: in SPI mode ILI9488 only supports RGB666 mode, which is currently not supported) + * + * any probably many more + * + */ + +#ifndef LV_LCD_GENERIC_MIPI_H +#define LV_LCD_GENERIC_MIPI_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../../display/lv_display.h" + +#if LV_USE_GENERIC_MIPI + +/********************* + * DEFINES + *********************/ + +/* MIPI DCS (Display Command Set) v1.02.00 User Command Set */ +#define LV_LCD_CMD_NOP 0x00 /* No Operation */ +#define LV_LCD_CMD_SOFT_RESET 0x01 /* Software Reset */ +#define LV_LCD_CMD_GET_POWER_MODE 0x0A /* Get the current power mode */ +#define LV_LCD_CMD_GET_ADDRESS_MODE 0x0B /* Get the data order for transfers from the Host to the display module and from the frame memory to the display device */ +#define LV_LCD_CMD_GET_PIXEL_FORMAT 0x0C /* Get the current pixel format */ +#define LV_LCD_CMD_GET_DISPLAY_MODE 0x0D /* Get the current display mode from the peripheral */ +#define LV_LCD_CMD_GET_SIGNAL_MODE 0x0E /* Get display module signaling mode */ +#define LV_LCD_CMD_GET_DIAGNOSTIC_RESULT 0x0F /* Get Peripheral Self-Diagnostic Result */ +#define LV_LCD_CMD_ENTER_SLEEP_MODE 0x10 /* Power for the display panel is off */ +#define LV_LCD_CMD_EXIT_SLEEP_MODE 0x11 /* Power for the display panel is on */ +#define LV_LCD_CMD_ENTER_PARTIAL_MODE 0x12 /* Part of the display area is used for image display */ +#define LV_LCD_CMD_ENTER_NORMAL_MODE 0x13 /* The whole display area is used for image display */ +#define LV_LCD_CMD_EXIT_INVERT_MODE 0x20 /* Displayed image colors are not inverted */ +#define LV_LCD_CMD_ENTER_INVERT_MODE 0x21 /* Displayed image colors are inverted */ +#define LV_LCD_CMD_SET_GAMMA_CURVE 0x26 /* Selects the gamma curve used by the display device */ +#define LV_LCD_CMD_SET_DISPLAY_OFF 0x28 /* Blanks the display device */ +#define LV_LCD_CMD_SET_DISPLAY_ON 0x29 /* Show the image on the display device */ +#define LV_LCD_CMD_SET_COLUMN_ADDRESS 0x2A /* Set the column extent */ +#define LV_LCD_CMD_SET_PAGE_ADDRESS 0x2B /* Set the page extent */ +#define LV_LCD_CMD_WRITE_MEMORY_START 0x2C /* Transfer image data from the Host Processor to the peripheral starting at the location provided by set_column_address and set_page_address */ +#define LV_LCD_CMD_READ_MEMORY_START 0x2E /* Transfer image data from the peripheral to the Host Processor interface starting at the location provided by set_column_address and set_page_address */ +#define LV_LCD_CMD_SET_PARTIAL_ROWS 0x30 /* Defines the number of rows in the partial display area on the display device */ +#define LV_LCD_CMD_SET_PARTIAL_COLUMNS 0x31 /* Defines the number of columns in the partial display area on the display device */ +#define LV_LCD_CMD_SET_SCROLL_AREA 0x33 /* Defines the vertical scrolling and fixed area on display device */ +#define LV_LCD_CMD_SET_TEAR_OFF 0x34 /* Synchronization information is not sent from the display module to the host processor */ +#define LV_LCD_CMD_SET_TEAR_ON 0x35 /* Synchronization information is sent from the display module to the host processor at the start of VFP */ +#define LV_LCD_CMD_SET_ADDRESS_MODE 0x36 /* Set the data order for transfers from the Host to the display module and from the frame memory to the display device */ +#define LV_LCD_CMD_SET_SCROLL_START 0x37 /* Defines the vertical scrolling starting point */ +#define LV_LCD_CMD_EXIT_IDLE_MODE 0x38 /* Full color depth is used on the display panel */ +#define LV_LCD_CMD_ENTER_IDLE_MODE 0x39 /* Reduced color depth is used on the display panel */ +#define LV_LCD_CMD_SET_PIXEL_FORMAT 0x3A /* Defines how many bits per pixel are used in the interface */ +#define LV_LCD_CMD_WRITE_MEMORY_CONTINUE 0x3C /* Transfer image information from the Host Processor interface to the peripheral from the last written location */ +#define LV_LCD_CMD_READ_MEMORY_CONTINUE 0x3E /* Read image data from the peripheral continuing after the last read_memory_continue or read_memory_start */ +#define LV_LCD_CMD_SET_TEAR_SCANLINE 0x44 /* Synchronization information is sent from the display module to the host processor when the display device refresh reaches the provided scanline */ +#define LV_LCD_CMD_GET_SCANLINE 0x45 /* Get the current scanline */ +#define LV_LCD_CMD_READ_DDB_CONTINUE 0xA8 /* Continue reading the DDB from the last read location */ +#define LV_LCD_CMD_READ_DDB_START 0xA1 /* Read the DDB from the provided location */ + +/* address mode flag masks */ +#define LV_LCD_MASK_FLIP_VERTICAL (1 << 0) /* This bit flips the image shown on the display device top to bottom. No change is made to the frame memory */ +#define LV_LCD_MASK_FLIP_HORIZONTAL (1 << 1) /* This bit flips the image shown on the display device left to right. No change is made to the frame memory */ +#define LV_LCD_MASK_DATA_LATCH_DATA_ORDER (1 << 2) /* Display Data Latch Order */ +#define LV_LCD_MASK_RGB_ORDER (1 << 3) /* RGB/BGR Order */ +#define LV_LCD_MASK_LINE_ADDRESS_ORDER (1 << 4) /* Line Address Order */ +#define LV_LCD_MASK_PAGE_COLUMN_ORDER (1 << 5) /* Page/Column Order */ +#define LV_LCD_MASK_COLUMN_ADDRESS_ORDER (1 << 6) /* Column Address Order */ +#define LV_LCD_MASK_PAGE_ADDRESS_ORDER (1 << 7) /* Page Address Order */ + +#define LV_LCD_BIT_FLIP_VERTICAL__NOT_FLIPPED 0 +#define LV_LCD_BIT_FLIP_VERTICAL__FLIPPED LV_LCD_MASK_FLIP_VERTICAL /* This bit flips the image shown on the display device top to bottom. No change is made to the frame memory */ +#define LV_LCD_BIT_FLIP_HORIZONTAL__NOT_FLIPPED 0 +#define LV_LCD_BIT_FLIP_HORIZONTAL__FLIPPED LV_LCD_MASK_FLIP_HORIZONTAL /* This bit flips the image shown on the display device left to right. No change is made to the frame memory */ +#define LV_LCD_BIT_DATA_LATCH_DATA_ORDER__LTOR 0 /* Display Data Latch Order: LCD Refresh Left to Right */ +#define LV_LCD_BIT_DATA_LATCH_DATA_ORDER__RTOL LV_LCD_MASK_DATA_LATCH_DATA_ORDER /* Display Data Latch Order: LCD Refresh Right to Left */ +#define LV_LCD_BIT_RGB_ORDER__RGB 0 /* RGB/BGR Order: RGB */ +#define LV_LCD_BIT_RGB_ORDER__BGR LV_LCD_MASK_RGB_ORDER /* RGB/BGR Order: BGR */ +#define LV_LCD_BIT_LINE_ADDRESS_ORDER__TTOB 0 /* Line Address Order: LCD Refresh Top to Bottom */ +#define LV_LCD_BIT_LINE_ADDRESS_ORDER__BTOT LV_LCD_MASK_LINE_ADDRESS_ORDER /* Line Address Order: LCD Refresh Botton to Top */ +#define LV_LCD_BIT_PAGE_COLUMN_ORDER__NORMAL 0 /* Page/Column Order: Normal Mode */ +#define LV_LCD_BIT_PAGE_COLUMN_ORDER__REVERSE LV_LCD_MASK_PAGE_COLUMN_ORDER /* Page/Column Order: Reverse Mode */ +#define LV_LCD_BIT_COLUMN_ADDRESS_ORDER__LTOR 0 /* Column Address Order: Left to Right */ +#define LV_LCD_BIT_COLUMN_ADDRESS_ORDER__RTOL LV_LCD_MASK_COLUMN_ADDRESS_ORDER /* Column Address Order: Right to Left */ +#define LV_LCD_BIT_PAGE_ADDRESS_ORDER__TTOB 0 /* Page Address Order: Top to Bottom */ +#define LV_LCD_BIT_PAGE_ADDRESS_ORDER__BTOT LV_LCD_MASK_PAGE_ADDRESS_ORDER /* Page Address Order: Bottom to Top */ + +/* predefined gamma curves */ +#define LV_LCD_GAMMA_2_2 0x01 /* 2.2 */ +#define LV_LCD_GAMMA_1_8 0x02 /* 1.8 */ +#define LV_LCD_GAMMA_2_5 0x04 /* 2.5 */ +#define LV_LCD_GAMMA_1_0 0x08 /* 1.0 */ + +/* common pixel formats */ +#define LV_LCD_PIXEL_FORMAT_RGB565 0x55 /* bus: 16 bits, pixel: 16 bits */ +#define LV_LCD_PIXEL_FORMAT_RGB666 0x66 /* bus: 18 bits, pixel: 18 bits */ + +/* flags for lv_lcd_xxx_create() */ +#define LV_LCD_FLAG_NONE 0x00000000UL +#define LV_LCD_FLAG_MIRROR_X 0x00000001UL +#define LV_LCD_FLAG_MIRROR_Y 0x00000002UL +#define LV_LCD_FLAG_BGR 0x00000008UL +#define LV_LCD_FLAG_RGB666 0x00000010UL + +/* command list */ +#define LV_LCD_CMD_DELAY_MS 0xff +#define LV_LCD_CMD_EOF 0xff + +/********************** + * TYPEDEFS + **********************/ + +/** + * Configuration flags for lv_lcd_xxx_create() + * + */ +typedef uint32_t lv_lcd_flag_t; + +/** + * Prototype of a platform-dependent callback to transfer commands and data to the LCD controller. + * @param disp display object + * @param cmd command buffer (can handle 16 bit commands as well) + * @param cmd_size number of bytes of the command + * @param param parameter buffer + * @param param_size number of bytes of the parameters + */ +typedef void (*lv_lcd_send_cmd_cb_t)(lv_display_t * disp, const uint8_t * cmd, size_t cmd_size, const uint8_t * param, + size_t param_size); + +/** + * Prototype of a platform-dependent callback to transfer pixel data to the LCD controller. + * @param disp display object + * @param cmd command buffer (can handle 16 bit commands as well) + * @param cmd_size number of bytes of the command + * @param param parameter buffer + * @param param_size number of bytes of the parameters + */ +typedef void (*lv_lcd_send_color_cb_t)(lv_display_t * disp, const uint8_t * cmd, size_t cmd_size, uint8_t * param, + size_t param_size); + +/** + * Generic MIPI compatible LCD driver + */ +typedef struct { + lv_display_t * disp; /* the associated LVGL display object */ + lv_lcd_send_cmd_cb_t send_cmd; /* platform-specific implementation to send a command to the LCD controller */ + lv_lcd_send_color_cb_t send_color; /* platform-specific implementation to send pixel data to the LCD controller */ + uint16_t x_gap; /* x offset of the (0,0) pixel in VRAM */ + uint16_t y_gap; /* y offset of the (0,0) pixel in VRAM */ + uint8_t madctl_reg; /* current value of MADCTL register */ + uint8_t colmod_reg; /* current value of COLMOD register */ + bool mirror_x; + bool mirror_y; + bool swap_xy; +} lv_lcd_generic_mipi_driver_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a MIPI DCS compatible LCD display + * @param hor_res horizontal resolution + * @param ver_res vertical resolution + * @param flags default configuration settings (mirror, RGB ordering, etc.) + * @param send_cmd platform-dependent function to send a command to the LCD controller (usually uses polling transfer) + * @param send_color platform-dependent function to send pixel data to the LCD controller (usually uses DMA transfer: must implement a 'ready' callback) + * @return pointer to the created display + */ +lv_display_t * lv_lcd_generic_mipi_create(uint32_t hor_res, uint32_t ver_res, lv_lcd_flag_t flags, + lv_lcd_send_cmd_cb_t send_cmd_cb, lv_lcd_send_color_cb_t send_color_cb); + +/** + * Set gap, i.e., the offset of the (0,0) pixel in the VRAM + * @param disp display object + * @param x x offset + * @param y y offset + */ +void lv_lcd_generic_mipi_set_gap(lv_display_t * disp, uint16_t x, uint16_t y); + +/** + * Set color inversion + * @param disp display object + * @param invert false: normal, true: invert + */ +void lv_lcd_generic_mipi_set_invert(lv_display_t * disp, bool invert); + +/** + * Set address mode + * @param disp display object + * @param mirror_x horizontal mirror (false: normal, true: mirrored) + * @param mirror_y vertical mirror (false: normal, true: mirrored) + * @param swap_xy swap axes (false: normal, true: swap) + * @param bgr RGB/BGR order (false: RGB, true: BGR) + */ +void lv_lcd_generic_mipi_set_address_mode(lv_display_t * disp, bool mirror_x, bool mirror_y, bool swap_xy, bool bgr); + +/** + * Set gamma curve + * @param disp display object + * @param gamma gamma curve + */ +void lv_lcd_generic_mipi_set_gamma_curve(lv_display_t * disp, uint8_t gamma); + +/** + * Send list of commands. + * @param disp display object + * @param cmd_list controller and panel-specific commands + */ +void lv_lcd_generic_mipi_send_cmd_list(lv_display_t * disp, const uint8_t * cmd_list); + +/********************** + * OTHERS + **********************/ + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_USE_GENERIC_MIPI*/ + +#endif /* LV_LCD_GENERIC_MIPI_H */ diff --git a/src/dev/display/st7735/lv_st7735.c b/src/dev/display/st7735/lv_st7735.c new file mode 100644 index 000000000..6b5e62dcb --- /dev/null +++ b/src/dev/display/st7735/lv_st7735.c @@ -0,0 +1,113 @@ +/** + * @file lv_st7735.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_st7735.h" + +#if LV_USE_ST7735 + +/********************* + * DEFINES + *********************/ + +#define CMD_GAMSET 0x26 + +#define CMD_FRMCTR1 0xB1 +#define CMD_FRMCTR2 0xB2 +#define CMD_FRMCTR3 0xB3 +#define CMD_INVCTR 0xB4 +#define CMD_DISSET5 0xB6 + +#define CMD_PWCTR1 0xC0 +#define CMD_PWCTR2 0xC1 +#define CMD_PWCTR3 0xC2 +#define CMD_PWCTR4 0xC3 +#define CMD_PWCTR5 0xC4 +#define CMD_VMCTR1 0xC5 +#define CMD_VMOFCTR 0xC7 + +#define CMD_NVFCTR1 0xD9 + +#define CMD_GMCTRP1 0xE0 +#define CMD_GMCTRN1 0xE1 + +#define CMD_PWCTR6 0xFC + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC CONSTANTS + **********************/ + +/* init commands for buydisplay.com ER-TFTM018-3 */ +static const uint8_t init_cmd_list[] = { + 0xB1, 3, 0x05, 0x3C, 0x3C, + 0xB2, 3, 0x05, 0x3C, 0x3C, + 0xB3, 6, 0x05, 0x3C, 0x3C, 0x05, 0x3C, 0x3C, + 0xB4, 1, 0x03, + 0xC0, 3, 0x28, 0x08, 0x04, + 0xC1, 1, 0XC0, + 0xC2, 2, 0x0D, 0x00, + 0xC3, 2, 0x8D, 0x2A, + 0xC4, 2, 0x8D, 0xEE, + 0xC5, 1, 0x10, + 0xE0, 16, 0x04, 0x22, 0x07, 0x0A, 0x2E, 0x30, 0x25, 0x2A, 0x28, 0x26, 0x2E, 0x3A, 0x00, 0x01, 0x03, 0x13, + 0xE1, 16, 0x04, 0x16, 0x06, 0x0D, 0x2D, 0x26, 0x23, 0x27, 0x27, 0x25, 0x2D, 0x3B, 0x00, 0x01, 0x04, 0x13, + LV_LCD_CMD_DELAY_MS, LV_LCD_CMD_EOF +}; + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_display_t * lv_st7735_create(uint32_t hor_res, uint32_t ver_res, lv_lcd_flag_t flags, + lv_st7735_send_cmd_cb_t send_cmd_cb, lv_st7735_send_color_cb_t send_color_cb) +{ + lv_display_t * disp = lv_lcd_generic_mipi_create(hor_res, ver_res, flags, send_cmd_cb, send_color_cb); + lv_lcd_generic_mipi_send_cmd_list(disp, init_cmd_list); + return disp; +} + +void lv_st7735_set_gap(lv_display_t * disp, uint16_t x, uint16_t y) +{ + lv_lcd_generic_mipi_set_gap(disp, x, y); +} + +void lv_st7735_set_invert(lv_display_t * disp, bool invert) +{ + lv_lcd_generic_mipi_set_invert(disp, invert); +} + +void lv_st7735_set_gamma_curve(lv_display_t * disp, uint8_t gamma) +{ + lv_lcd_generic_mipi_set_gamma_curve(disp, gamma); +} + +void lv_st7735_send_cmd_list(lv_display_t * disp, const uint8_t * cmd_list) +{ + lv_lcd_generic_mipi_send_cmd_list(disp, cmd_list); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_ST7735*/ diff --git a/src/dev/display/st7735/lv_st7735.h b/src/dev/display/st7735/lv_st7735.h new file mode 100644 index 000000000..062cfe31b --- /dev/null +++ b/src/dev/display/st7735/lv_st7735.h @@ -0,0 +1,93 @@ +/* + * lv_st7735.h + * + * This driver is just a wrapper around the generic MIPI compatible LCD controller driver + * + */ + +#ifndef LV_ST7735_H +#define LV_ST7735_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../lcd/lv_lcd_generic_mipi.h" + +#if LV_USE_ST7735 + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef lv_lcd_send_cmd_cb_t lv_st7735_send_cmd_cb_t; +typedef lv_lcd_send_color_cb_t lv_st7735_send_color_cb_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create an LCD display with ST7735 driver + * @param hor_res horizontal resolution + * @param ver_res vertical resolution + * @param flags default configuration settings (mirror, RGB ordering, etc.) + * @param send_cmd platform-dependent function to send a command to the LCD controller (usually uses polling transfer) + * @param send_color platform-dependent function to send pixel data to the LCD controller (usually uses DMA transfer: must implement a 'ready' callback) + * @return pointer to the created display + */ +lv_display_t * lv_st7735_create(uint32_t hor_res, uint32_t ver_res, lv_lcd_flag_t flags, + lv_st7735_send_cmd_cb_t send_cmd_cb, lv_st7735_send_color_cb_t send_color_cb); + +/** + * Set gap, i.e., the offset of the (0,0) pixel in the VRAM + * @param disp display object + * @param x x offset + * @param y y offset + */ +void lv_st7735_set_gap(lv_display_t * disp, uint16_t x, uint16_t y); + +/** + * Set color inversion + * @param disp display object + * @param invert false: normal, true: invert + */ +void lv_st7735_set_invert(lv_display_t * disp, bool invert); + +/** + * Set gamma curve + * @param disp display object + * @param gamma gamma curve + */ +void lv_st7735_set_gamma_curve(lv_display_t * disp, uint8_t gamma); + +/** + * Send list of commands. + * @param disp display object + * @param cmd_list controller and panel-specific commands + */ +void lv_st7735_send_cmd_list(lv_display_t * disp, const uint8_t * cmd_list); + +/********************** + * OTHERS + **********************/ + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_ST7735*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /* LV_ST7735_H */ diff --git a/src/dev/display/st7789/lv_st7789.c b/src/dev/display/st7789/lv_st7789.c new file mode 100644 index 000000000..76d08bfb2 --- /dev/null +++ b/src/dev/display/st7789/lv_st7789.c @@ -0,0 +1,116 @@ +/** + * @file lv_st7789.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_st7789.h" + +#if LV_USE_ST7789 + +/********************* + * DEFINES + *********************/ + +#define CMD_FRMCTR1 0xB1 +#define CMD_FRMCTR2 0xB2 +#define CMD_FRMCTR3 0xB3 +#define CMD_INVCTR 0xB4 +#define CMD_DFUNCTR 0xB6 +#define CMD_ETMOD 0xB7 +#define CMD_PWCTR1 0xC0 +#define CMD_PWCTR2 0xC1 +#define CMD_PWCTR3 0xC2 +#define CMD_PWCTR4 0xC3 +#define CMD_PWCTR5 0xC4 +#define CMD_VMCTR 0xC5 +#define CMD_GMCTRP1 0xE0 +#define CMD_GMCTRN1 0xE1 +#define CMD_DOCA 0xE8 +#define CMD_CSCON 0xF0 + +#define CMD_RAMCTRL 0xB0 +#define CMD_PORCTRL 0xB2 /* Porch control */ +#define CMD_GCTRL 0xB7 /* Gate control */ +#define CMD_VCOMS 0xBB /* VCOMS setting */ +#define CMD_LCMCTRL 0xC0 /* LCM control */ +#define CMD_VDVVRHEN 0xC2 /* VDV and VRH command enable */ +#define CMD_VRHS 0xC3 /* VRH set */ +#define CMD_VDVSET 0xC4 /* VDV setting */ +#define CMD_FRCTR2 0xC6 /* FR Control 2 */ +#define CMD_PWCTRL1 0xD0 /* Power control 1 */ +#define CMD_PVGAMCTRL 0xE0 /* Positive Gamma Correction */ +#define CMD_NVGAMCTRL 0xE1 /* Negative Gamma Correction */ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC CONSTANTS + **********************/ + +/* init commands based on LovyanGFX ST7789 driver */ +static const uint8_t init_cmd_list[] = { + CMD_GCTRL, 1, 0x44, /* GCTRL -- panel dependent */ + CMD_VCOMS, 1, 0x24, /* VCOMS -- panel dependent */ + CMD_VRHS, 1, 0x13, /* VRHS - panel dependent */ + CMD_PWCTRL1, 2, 0xa4, 0xa1, + CMD_RAMCTRL, 2, 0x00, 0xC0, /* controls mapping of RGB565 to RGB666 */ + CMD_PVGAMCTRL, 14, 0xd0, 0x00, 0x02, 0x07, 0x0a, 0x28, 0x32, 0x44, 0x42, 0x06, 0x0e, 0x12, 0x14, 0x17, + CMD_NVGAMCTRL, 14, 0xd0, 0x00, 0x02, 0x07, 0x0a, 0x28, 0x31, 0x54, 0x47, 0x0e, 0x1c, 0x17, 0x1b, 0x1e, + LV_LCD_CMD_SET_GAMMA_CURVE, 1, 0x01, + LV_LCD_CMD_DELAY_MS, LV_LCD_CMD_EOF +}; + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_display_t * lv_st7789_create(uint32_t hor_res, uint32_t ver_res, lv_lcd_flag_t flags, + lv_st7789_send_cmd_cb_t send_cmd_cb, lv_st7789_send_color_cb_t send_color_cb) +{ + lv_display_t * disp = lv_lcd_generic_mipi_create(hor_res, ver_res, flags, send_cmd_cb, send_color_cb); + lv_lcd_generic_mipi_send_cmd_list(disp, init_cmd_list); + return disp; +} + +void lv_st7789_set_gap(lv_display_t * disp, uint16_t x, uint16_t y) +{ + lv_lcd_generic_mipi_set_gap(disp, x, y); +} + +void lv_st7789_set_invert(lv_display_t * disp, bool invert) +{ + lv_lcd_generic_mipi_set_invert(disp, invert); +} + +void lv_st7789_set_gamma_curve(lv_display_t * disp, uint8_t gamma) +{ + lv_lcd_generic_mipi_set_gamma_curve(disp, gamma); +} + +void lv_st7789_send_cmd_list(lv_display_t * disp, const uint8_t * cmd_list) +{ + lv_lcd_generic_mipi_send_cmd_list(disp, cmd_list); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_ST7789*/ diff --git a/src/dev/display/st7789/lv_st7789.h b/src/dev/display/st7789/lv_st7789.h new file mode 100644 index 000000000..5cd301948 --- /dev/null +++ b/src/dev/display/st7789/lv_st7789.h @@ -0,0 +1,93 @@ +/* + * lv_st7789.h + * + * This driver is just a wrapper around the generic MIPI compatible LCD controller driver + * + */ + +#ifndef LV_ST7789_H +#define LV_ST7789_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../lcd/lv_lcd_generic_mipi.h" + +#if LV_USE_ST7789 + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef lv_lcd_send_cmd_cb_t lv_st7789_send_cmd_cb_t; +typedef lv_lcd_send_color_cb_t lv_st7789_send_color_cb_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create an LCD display with ST7789 driver + * @param hor_res horizontal resolution + * @param ver_res vertical resolution + * @param flags default configuration settings (mirror, RGB ordering, etc.) + * @param send_cmd platform-dependent function to send a command to the LCD controller (usually uses polling transfer) + * @param send_color platform-dependent function to send pixel data to the LCD controller (usually uses DMA transfer: must implement a 'ready' callback) + * @return pointer to the created display + */ +lv_display_t * lv_st7789_create(uint32_t hor_res, uint32_t ver_res, lv_lcd_flag_t flags, + lv_st7789_send_cmd_cb_t send_cmd_cb, lv_st7789_send_color_cb_t send_color_cb); + +/** + * Set gap, i.e., the offset of the (0,0) pixel in the VRAM + * @param disp display object + * @param x x offset + * @param y y offset + */ +void lv_st7789_set_gap(lv_display_t * disp, uint16_t x, uint16_t y); + +/** + * Set color inversion + * @param disp display object + * @param invert false: normal, true: invert + */ +void lv_st7789_set_invert(lv_display_t * disp, bool invert); + +/** + * Set gamma curve + * @param disp display object + * @param gamma gamma curve + */ +void lv_st7789_set_gamma_curve(lv_display_t * disp, uint8_t gamma); + +/** + * Send list of commands. + * @param disp display object + * @param cmd_list controller and panel-specific commands + */ +void lv_st7789_send_cmd_list(lv_display_t * disp, const uint8_t * cmd_list); + +/********************** + * OTHERS + **********************/ + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_USE_ST7789*/ + +#endif //LV_ST7789_H diff --git a/src/dev/display/st7796/lv_st7796.c b/src/dev/display/st7796/lv_st7796.c new file mode 100644 index 000000000..ef5be00fd --- /dev/null +++ b/src/dev/display/st7796/lv_st7796.c @@ -0,0 +1,122 @@ +/** + * @file lv_st7796.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_st7796.h" + +#if LV_USE_ST7796 + +/********************* + * DEFINES + *********************/ + +#define CMD_FRMCTR1 0xB1 +#define CMD_FRMCTR2 0xB2 +#define CMD_FRMCTR3 0xB3 +#define CMD_INVCTR 0xB4 +#define CMD_DFUNCTR 0xB6 +#define CMD_ETMOD 0xB7 +#define CMD_PWCTR1 0xC0 +#define CMD_PWCTR2 0xC1 +#define CMD_PWCTR3 0xC2 +#define CMD_PWCTR4 0xC3 +#define CMD_PWCTR5 0xC4 +#define CMD_VMCTR 0xC5 +#define CMD_GMCTRP1 0xE0 +#define CMD_GMCTRN1 0xE1 +#define CMD_DOCA 0xE8 +#define CMD_CSCON 0xF0 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC CONSTANTS + **********************/ + +/* init commands based on LovyanGFX */ +static const uint8_t init_cmd_list[] = { + CMD_CSCON, 1, 0xC3, /* Enable extension command 2 partI */ + CMD_CSCON, 1, 0x96, /* Enable extension command 2 partII */ + CMD_INVCTR, 1, 0x01, /* 1-dot inversion */ + CMD_DFUNCTR, 3, 0x80, /* Display Function Control: Bypass */ + 0x22, /* Source Output Scan from S1 to S960, Gate Output scan from G1 to G480, scan cycle = 2 */ + 0x3B, /* LCD Drive Line = 8 * (59 + 1) */ + CMD_DOCA, 8, 0x40, 0x8A, 0x00, 0x00, + 0x29, /* Source equalizing period time = 22.5 us */ + 0x19, /* Timing for "Gate start" = 25 (Tclk) */ + 0xA5, /* Timing for "Gate End" = 37 (Tclk), Gate driver EQ function ON */ + 0x33, + CMD_PWCTR2, 1, 0x06, /* Power control2: VAP(GVDD) = 3.85 + (vcom + vcom offset), VAN(GVCL) = -3.85 + (vcom + vcom offset) */ + CMD_PWCTR3, 1, 0xA7, /* Power control 3: Source driving current level = low, Gamma driving current level = High */ + CMD_VMCTR, 1, 0x18, /* VCOM Control: VCOM = 0.9 */ + LV_LCD_CMD_DELAY_MS, 12, /* delay 120 ms */ + CMD_GMCTRP1, 14, /* Gamma */ + 0xF0, 0x09, 0x0B, 0x06, 0x04, 0x15, 0x2F, + 0x54, 0x42, 0x3C, 0x17, 0x14, 0x18, 0x1B, + CMD_GMCTRN1, 14, + 0xE0, 0x09, 0x0B, 0x06, 0x04, 0x03, 0x2B, + 0x43, 0x42, 0x3B, 0x16, 0x14, 0x17, 0x1B, + LV_LCD_CMD_DELAY_MS, 12, /* delay 120 ms */ + CMD_CSCON, 1, 0x3C, /* Disable extension command 2 partI */ + CMD_CSCON, 1, 0x69, /* Disable extension command 2 partII */ + LV_LCD_CMD_DELAY_MS, LV_LCD_CMD_EOF +}; + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_display_t * lv_st7796_create(uint32_t hor_res, uint32_t ver_res, lv_lcd_flag_t flags, + lv_st7796_send_cmd_cb_t send_cmd_cb, lv_st7796_send_color_cb_t send_color_cb) +{ + lv_display_t * disp = lv_lcd_generic_mipi_create(hor_res, ver_res, flags, send_cmd_cb, send_color_cb); + lv_lcd_generic_mipi_send_cmd_list(disp, init_cmd_list); + return disp; +} + +void lv_st7796_set_gap(lv_display_t * disp, uint16_t x, uint16_t y) +{ + lv_lcd_generic_mipi_set_gap(disp, x, y); +} + +void lv_st7796_set_invert(lv_display_t * disp, bool invert) +{ + lv_lcd_generic_mipi_set_invert(disp, invert); +} + +void lv_st7796_set_gamma_curve(lv_display_t * disp, uint8_t gamma) +{ + /* NOTE: the generic method is not supported on ST7796, TODO: implement gamma tables */ + LV_UNUSED(disp); + LV_UNUSED(gamma); +} + +void lv_st7796_send_cmd_list(lv_display_t * disp, const uint8_t * cmd_list) +{ + lv_lcd_generic_mipi_send_cmd_list(disp, cmd_list); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_ST7796*/ + diff --git a/src/dev/display/st7796/lv_st7796.h b/src/dev/display/st7796/lv_st7796.h new file mode 100644 index 000000000..c34dfe681 --- /dev/null +++ b/src/dev/display/st7796/lv_st7796.h @@ -0,0 +1,93 @@ +/* + * lv_st7796.h + * + * This driver is just a wrapper around the generic MIPI compatible LCD controller driver + * + */ + +#ifndef LV_ST7796_H +#define LV_ST7796_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../lcd/lv_lcd_generic_mipi.h" + +#if LV_USE_ST7796 + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef lv_lcd_send_cmd_cb_t lv_st7796_send_cmd_cb_t; +typedef lv_lcd_send_color_cb_t lv_st7796_send_color_cb_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create an LCD display with ST7796 driver + * @param hor_res horizontal resolution + * @param ver_res vertical resolution + * @param flags default configuration settings (mirror, RGB ordering, etc.) + * @param send_cmd platform-dependent function to send a command to the LCD controller (usually uses polling transfer) + * @param send_color platform-dependent function to send pixel data to the LCD controller (usually uses DMA transfer: must implement a 'ready' callback) + * @return pointer to the created display + */ +lv_display_t * lv_st7796_create(uint32_t hor_res, uint32_t ver_res, lv_lcd_flag_t flags, + lv_st7796_send_cmd_cb_t send_cmd_cb, lv_st7796_send_color_cb_t send_color_cb); + +/** + * Set gap, i.e., the offset of the (0,0) pixel in the VRAM + * @param disp display object + * @param x x offset + * @param y y offset + */ +void lv_st7796_set_gap(lv_display_t * disp, uint16_t x, uint16_t y); + +/** + * Set color inversion + * @param disp display object + * @param invert false: normal, true: invert + */ +void lv_st7796_set_invert(lv_display_t * disp, bool invert); + +/** + * Set gamma curve + * @param disp display object + * @param gamma gamma curve + */ +void lv_st7796_set_gamma_curve(lv_display_t * disp, uint8_t gamma); + +/** + * Send list of commands. + * @param disp display object + * @param cmd_list controller and panel-specific commands + */ +void lv_st7796_send_cmd_list(lv_display_t * disp, const uint8_t * cmd_list); + +/********************** + * OTHERS + **********************/ + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_USE_ST7796*/ + +#endif /* LV_ST7796_H */ diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index 7eacdaff4..758d34194 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -2826,6 +2826,45 @@ #endif #endif +/*Drivers for LCD devices connected via SPI/parallel port*/ +#ifndef LV_USE_ST7735 + #ifdef CONFIG_LV_USE_ST7735 + #define LV_USE_ST7735 CONFIG_LV_USE_ST7735 + #else + #define LV_USE_ST7735 0 + #endif +#endif +#ifndef LV_USE_ST7789 + #ifdef CONFIG_LV_USE_ST7789 + #define LV_USE_ST7789 CONFIG_LV_USE_ST7789 + #else + #define LV_USE_ST7789 0 + #endif +#endif +#ifndef LV_USE_ST7796 + #ifdef CONFIG_LV_USE_ST7796 + #define LV_USE_ST7796 CONFIG_LV_USE_ST7796 + #else + #define LV_USE_ST7796 0 + #endif +#endif +#ifndef LV_USE_ILI9341 + #ifdef CONFIG_LV_USE_ILI9341 + #define LV_USE_ILI9341 CONFIG_LV_USE_ILI9341 + #else + #define LV_USE_ILI9341 0 + #endif +#endif + +#ifndef LV_USE_GENERIC_MIPI + #ifdef CONFIG_LV_USE_GENERIC_MIPI + #define LV_USE_GENERIC_MIPI CONFIG_LV_USE_GENERIC_MIPI + #else + #define LV_USE_GENERIC_MIPI (LV_USE_ST7735 | LV_USE_ST7789 | LV_USE_ST7796 | LV_USE_ILI9341) + #endif +#endif + + /*================== * EXAMPLES *==================*/ diff --git a/tests/src/lv_test_conf_full.h b/tests/src/lv_test_conf_full.h index b18c0f193..114a58fc4 100644 --- a/tests/src/lv_test_conf_full.h +++ b/tests/src/lv_test_conf_full.h @@ -99,6 +99,11 @@ #define LV_CACHE_DEF_SIZE (10 * 1024 * 1024) +#define LV_USE_ILI9341 1 +#define LV_USE_ST7735 1 +#define LV_USE_ST7789 1 +#define LV_USE_ST7796 1 + #define LV_USE_FREETYPE 1 #define LV_FREETYPE_CACHE_SIZE 768 #define LV_FREETYPE_USE_LVGL_PORT 0