1================================================= 2Generic MIPI DCS compatible LCD Controller driver 3================================================= 4 5Overview 6-------- 7 8From the `Wikipedia <https://en.wikipedia.org/wiki/MIPI_Alliance>`__: 9 10 `MIPI Alliance <https://www.mipi.org/>`__ is a global business alliance that develops technical specifications 11 for the mobile ecosystem, particularly smart phones but including mobile-influenced industries. MIPI was founded in 2003 by Arm, Intel, Nokia, Samsung, 12 STMicroelectronics and Texas Instruments. 13 14MIPI Alliance published a series of specifications related to display devices, including DBI (Display Bus Interface), DSI (Display Serial Interface) and DCS 15(Display Command Set). Usually when one talks about a MIPI-compatible display, one thinks of a device with a DSI serial interface. However, the Display Bus Interface specification 16includes a number of other, legacy interfaces, like SPI serial, or i8080-compatible parallel interface, which are often used to interface LCD displays to lower-end microcontrollers. 17Furthermore, the DCS specification contains a standard command set, which is supported by a large number of legacy TFT LCD controllers, including the popular Sitronix 18(ST7735, ST7789, ST7796) and Ilitek (ILI9341) SOCs. These commands provide a common interface to configure display orientation, color resolution, various power modes, and provide generic video memory access. On top 19of that standard command set each LCD controller chip has a number of vendor-specific commands to configure voltage generator levels, timings, or gamma curves. 20 21.. note:: 22 23 It is important to understand that this generic MIPI LCD driver is not a hardware driver for displays with the DSI ("MIPI") serial interface. Instead, it implements the MIPI DCS command set used in many LCD controllers with an SPI or i8080 bus, and provides a common framework for chip-specific display controllers. 24 25.. tip:: 26 Although this is a generic driver, it can be used to support compatible chips which do not have a specific driver. 27 28 29Prerequisites 30------------- 31 32There are no prerequisites. 33 34Configuring the driver 35---------------------- 36 37Enable the generic MIPI LCD driver support in lv_conf.h, by cmake compiler define or by KConfig 38 39.. code-block:: c 40 41 #define LV_USE_GENERIC_MIPI 1 42 43.. note:: 44 :c:macro:`LV_USE_GENERIC_MIPI` is automatically enabled when a compatible driver is enabled. 45 46Usage 47----- 48 49You need to implement two platform-dependent functions: 50 51.. code-block:: c 52 53 /* Send short command to the LCD. This function shall wait until the transaction finishes. */ 54 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) 55 { 56 ... 57 } 58 59 /* 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. */ 60 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) 61 { 62 ... 63 } 64 65The 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 66complete 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 67DMA to transmit data in the background. More sophisticated methods can be also implemented, like queuing transfers and scheduling them in the background. 68 69Please note that while display flushing is handled by the driver, it is the user's responsibility to call :cpp:func:`lv_display_flush_ready()` 70when the color transfer completes. In case of a DMA transfer this is usually done in a transfer ready callback. 71 72.. note:: 73 While it is acceptable to use a blocking implementation for the pixel transfer as well, performance will suffer. 74 75.. tip:: 76 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 77 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. 78 79Please 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 80to the display object as usual by calling :cpp:func:`lv_display_set_buffers()`. 81 82The 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 83implement 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. 84 85Example 86------- 87 88.. note:: 89 You can find a step-by-step guide and the actual implementation of the callbacks on an STM32F746 using STM32CubeIDE and the ST HAL libraries here: :ref:`lcd_stm32_guide` 90 91 92.. code-block:: c 93 94 #include "src/drivers/display/st7789/lv_st7789.h" 95 96 #define LCD_H_RES 240 97 #define LCD_V_RES 320 98 #define LCD_BUF_LINES 60 99 100 lv_display_t *my_disp; 101 102 ... 103 104 /* Initialize LCD I/O bus, reset LCD */ 105 static int32_t my_lcd_io_init(void) 106 { 107 ... 108 return HAL_OK; 109 } 110 111 /* Send command to the LCD controller */ 112 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) 113 { 114 ... 115 } 116 117 /* Send pixel data to the LCD controller */ 118 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) 119 { 120 ... 121 } 122 123 int main(int argc, char ** argv) 124 { 125 ... 126 127 /* Initialize LVGL */ 128 lv_init(); 129 130 /* Initialize LCD bus I/O */ 131 if (my_lcd_io_init() != 0) 132 return; 133 134 /* Create the LVGL display object and the LCD display driver */ 135 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); 136 137 /* Set display orientation to landscape */ 138 lv_display_set_rotation(my_disp, LV_DISPLAY_ROTATION_90); 139 140 /* Configure draw buffers, etc. */ 141 uint8_t * buf1 = NULL; 142 uint8_t * buf2 = NULL; 143 144 uint32_t buf_size = LCD_H_RES * LCD_BUF_LINES * lv_color_format_get_size(lv_display_get_color_format(my_disp)); 145 146 buf1 = lv_malloc(buf_size); 147 if(buf1 == NULL) { 148 LV_LOG_ERROR("display draw buffer malloc failed"); 149 return; 150 } 151 /* Allocate secondary buffer if needed */ 152 ... 153 154 lv_display_set_buffers(my_disp, buf1, buf2, buf_size, LV_DISPLAY_RENDER_MODE_PARTIAL); 155 156 ui_init(my_disp); 157 158 while(true) { 159 ... 160 161 /* Periodically call the lv_timer handler */ 162 lv_timer_handler(); 163 } 164 } 165 166Advanced topics 167--------------- 168 169Create flags 170^^^^^^^^^^^^ 171 172The 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 173default settings do not work for you. In particular, the generic MIPI driver accepts the following flags: 174 175.. code-block:: c 176 177 LV_LCD_FLAG_NONE 178 LV_LCD_FLAG_MIRROR_X 179 LV_LCD_FLAG_MIRROR_Y 180 LV_LCD_FLAG_BGR 181 182You can pass multiple flags by ORing them together, e.g., :c:macro:`LV_LCD_FLAG_MIRROR_X` ``|`` :c:macro:`LV_LCD_FLAG_BGR`. 183 184Custom command lists 185^^^^^^^^^^^^^^^^^^^^ 186 187While the chip-specific drivers do their best to initialize the LCD controller correctly, it is possible, that different TFT panels need different configurations. 188In particular a correct gamma setup is crucial for good color reproduction. Unfortunately, finding a good set of parameters is not easy. Usually the manufacturer 189of the panel provides some example code with recommended register settings. 190 191You 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 192the generic MIPI driver supports sending a custom command list to the controller. The commands must be put into a 'uint8_t' array: 193 194.. code-block:: c 195 196 static const uint8_t init_cmd_list[] = { 197 <command 1>, <number of parameters>, <parameter 1>, ... <parameter N>, 198 <command 2>, <number of parameters>, <parameter 1>, ... <parameter N>, 199 ... 200 LV_LCD_CMD_DELAY_MS, LV_LCD_CMD_EOF /* terminate list: this is required! */ 201 }; 202 203 ... 204 205 lv_lcd_generic_mipi_send_cmd_list(my_disp, init_cmd_list); 206 207You 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. 208To terminate the command list you must use a delay with a value of ``LV_LCD_CMD_EOF``, as shown above. 209 210See an actual example of sending a command list `here <https://github.com/lvgl/lvgl/blob/master/src/drivers/display/st7789/lv_st7789.c>`__. 211