{"id":4394,"date":"2026-04-26T07:05:31","date_gmt":"2026-04-26T07:05:31","guid":{"rendered":"https:\/\/blog.embeddedexpert.io\/?p=4394"},"modified":"2026-04-26T07:06:27","modified_gmt":"2026-04-26T07:06:27","slug":"stm32f429-discovery-display-guide-part-6-lvgl-integration","status":"publish","type":"post","link":"https:\/\/blog.embeddedexpert.io\/?p=4394","title":{"rendered":"STM32F429 Discovery Display Guide \u2013 Part 6: LvGL Integration"},"content":{"rendered":"\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"559\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/Gemini_Generated_Image_ggw43iggw43iggw4-1024x559.png\" alt=\"\" class=\"wp-image-4395\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/Gemini_Generated_Image_ggw43iggw43iggw4-1024x559.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/Gemini_Generated_Image_ggw43iggw43iggw4-300x164.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/Gemini_Generated_Image_ggw43iggw43iggw4-768x419.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/Gemini_Generated_Image_ggw43iggw43iggw4-1150x627.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/Gemini_Generated_Image_ggw43iggw43iggw4-750x409.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/Gemini_Generated_Image_ggw43iggw43iggw4-400x218.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/Gemini_Generated_Image_ggw43iggw43iggw4-250x136.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/Gemini_Generated_Image_ggw43iggw43iggw4.png 1408w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>In Part 6, we elevate our project by integrating the\u00a0<strong>Light and Versatile Graphics Library (LVGL)<\/strong>, transforming raw hardware drivers into a professional-grade graphical user interface. You will learn how to bridge your touch and display drivers with the library&#8217;s HAL and deploy a comprehensive widget demo to showcase the STM32F429\u2019s rendering capabilities.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>In this guide, we shall cover the following:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>STM32CubeMX sonfiguration.<\/li>\n\n\n\n<li>Firmware setup.<\/li>\n\n\n\n<li>Results.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">1. STM32CubeMX Configuration:<\/h2>\n\n\n\n<p>Open the project .ioc file in STM32CubeMX.<\/p>\n\n\n\n<p>From connectivity, select SPI5 and set the orescaler to 2. This will provide 45Mbps transfer speed as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"662\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-10-55-1024x662.png\" alt=\"\" class=\"wp-image-4396\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-10-55-1024x662.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-10-55-300x194.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-10-55-768x497.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-10-55-1536x993.png 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-10-55-2048x1324.png 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-10-55-1150x744.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-10-55-750x485.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-10-55-400x259.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-10-55-250x162.png 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Next, from DMA settings, enable DMA for SPI5_TX as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"662\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-22-52-1024x662.png\" alt=\"\" class=\"wp-image-4397\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-22-52-1024x662.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-22-52-300x194.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-22-52-768x497.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-22-52-1536x993.png 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-22-52-2048x1324.png 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-22-52-1150x744.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-22-52-750x485.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-22-52-400x259.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-22-52-250x162.png 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Finally, enable SPI5 global interrupt and generate code as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"662\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-24-47-1024x662.png\" alt=\"\" class=\"wp-image-4398\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-24-47-1024x662.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-24-47-300x194.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-24-47-768x497.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-24-47-1536x993.png 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-24-47-2048x1324.png 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-24-47-1150x744.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-24-47-750x485.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-24-47-400x259.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-24-47-250x162.png 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Thats all for the configuration.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2. Firmware Development:<\/h2>\n\n\n\n<p>First, head to this\u00a0<a href=\"https:\/\/github.com\/lvgl\/lvgl\" data-type=\"link\" data-id=\"https:\/\/github.com\/lvgl\/lvgl\" target=\"_blank\" rel=\"noreferrer noopener\">github repository<\/a> and download the latest version as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"525\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-29-25-1024x525.png\" alt=\"\" class=\"wp-image-4400\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-29-25-1024x525.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-29-25-300x154.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-29-25-768x394.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-29-25-1536x787.png 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-29-25-2048x1050.png 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-29-25-1150x589.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-29-25-750x384.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-29-25-400x205.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-29-25-250x128.png 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Once downloaded, extract it and rename it simply, lvgl and added to drivers folders as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"552\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-31-54-1024x552.png\" alt=\"\" class=\"wp-image-4401\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-31-54-1024x552.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-31-54-300x162.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-31-54-768x414.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-31-54-750x404.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-31-54-400x215.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-31-54-250x135.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-31-54.png 1036w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Next, we shall add LvGL folder to the build process.<\/p>\n\n\n\n<p>Right click on the project from project explorer and click on properties as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"580\" height=\"1024\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-33-55-580x1024.png\" alt=\"\" class=\"wp-image-4402\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-33-55-580x1024.png 580w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-33-55-170x300.png 170w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-33-55-768x1356.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-33-55-870x1536.png 870w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-33-55-750x1324.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-33-55-400x706.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-33-55-250x441.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-33-55.png 1124w\" sizes=\"(max-width: 580px) 100vw, 580px\" \/><\/figure>\n\n\n\n<p>From MCU\/MPU GCC compiler, include LvGL path as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"582\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-36-30-1024x582.png\" alt=\"\" class=\"wp-image-4403\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-36-30-1024x582.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-36-30-300x171.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-36-30-768x437.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-36-30-1536x873.png 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-36-30-2048x1164.png 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-36-30-1150x654.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-36-30-750x426.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-36-30-400x227.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-36-30-250x141.png 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Now, we are ready to develop the firmware.<\/p>\n\n\n\n<p>First, in driver folders, create new header file with name of lv_conf.h as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"526\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-39-53-1024x526.png\" alt=\"\" class=\"wp-image-4404\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-39-53-1024x526.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-39-53-300x154.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-39-53-768x394.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-39-53-750x385.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-39-53-400x205.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-39-53-250x128.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-39-53.png 1048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Copy the content of the following header file from <a href=\"https:\/\/github.com\/lvgl\/lvgl\/blob\/master\/lv_conf_template.h\" data-type=\"link\" data-id=\"https:\/\/github.com\/lvgl\/lvgl\/blob\/master\/lv_conf_template.h\">this repository.<\/a><\/p>\n\n\n\n<p>Once it has been copied, modify the following. First, enable it by setting #if to 1 as follows:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">#if 1 <\/pre><\/div>\n\n\n\n<p>By default, the color is set to RGB565, which is the correct one for our display.<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">#define LV_COLOR_DEPTH 16<\/pre><\/div>\n\n\n\n<p>Next, enable the demos as follows:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">#define LV_BUILD_DEMOS 1<\/pre><\/div>\n\n\n\n<p>Enable the widget demo as follows:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">#define LV_USE_DEMO_WIDGETS 1<\/pre><\/div>\n\n\n\n<p>Thats all for the lv_conf.h.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>Next, open <strong>ILI9341.h<\/strong> and add the following function:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">void ILI9341_Draw_Bitmap_DMA(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend, const uint8_t *Image);<\/pre><\/div>\n\n\n\n<p>This function shall use DMA to transfer the pixel data rather than polling mode.<\/p>\n\n\n\n<p>In <strong>ILI9341.c<\/strong> source file, add the following function:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">void ILI9341_Draw_Bitmap_DMA(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend, const uint8_t *Image)\n{\n\n\n\tuint32_t size = (Xend - Xstart + 1) * (Yend - Ystart + 1) * 2;\n\n\n\tILI9341_SetWindow(Xstart,Ystart,Xend,Yend);\n\n\n\n\tWriteCommand(0x2c);\n\n\tCS_Select();\n\n\tDC_HIGH();\n\tHAL_SPI_Transmit_DMA(&amp;hspi5, Image, size);\n\n\n}<\/pre><\/div>\n\n\n\n<p>Once the DMA transfer is completed, <strong>HAL_SPI_TxCpltCallback<\/strong> will be called.<\/p>\n\n\n\n<p>First, define this function as weak function. This will allow the user to override the function with his code as follows:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">__weak void ILI9341_Transfer_Completed(void);<\/pre><\/div>\n\n\n\n<p>For <strong>HAL_SPI_TxCpltCallback<\/strong>:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)\n{\n\tCS_Deselect();\n\tILI9341_Transfer_Completed();\n\n\n}<\/pre><\/div>\n\n\n\n<p>Simple, deselect the display and call <strong>ILI9341_Transfer_Completed<\/strong> function.<\/p>\n\n\n\n<p>Next, create new source and header files with the following names:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>display_lvgl_port.h<\/li>\n\n\n\n<li>display_lvgl_port.c<\/li>\n\n\n\n<li>touch_lvgl_port.h<\/li>\n\n\n\n<li>touch_lvgl_port.c<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"991\" height=\"1024\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-49-32-991x1024.png\" alt=\"\" class=\"wp-image-4405\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-49-32-991x1024.png 991w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-49-32-290x300.png 290w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-49-32-768x793.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-49-32-750x775.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-49-32-400x413.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-49-32-250x258.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-26_09-49-32.png 1034w\" sizes=\"(max-width: 991px) 100vw, 991px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>For display_lvgl_port.h, copy the content of the header file in <a href=\"https:\/\/github.com\/lvgl\/lvgl\/blob\/master\/examples\/porting\/lv_port_disp_template.h\" data-type=\"link\" data-id=\"https:\/\/github.com\/lvgl\/lvgl\/blob\/master\/examples\/porting\/lv_port_disp_template.h\">this repository.<\/a><\/p>\n\n\n\n<p>Hence, the content of the header file as follows:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">#if 1\n\n#ifndef LV_PORT_DISP_TEMPL_H\n#define LV_PORT_DISP_TEMPL_H\n\n#ifdef __cplusplus\nextern &quot;C&quot; {\n#endif\n\n\/*********************\n *      INCLUDES\n *********************\/\n\n#include &quot;lvgl.h&quot;\n\n\n\/*********************\n *      DEFINES\n *********************\/\n\n\/**********************\n *      TYPEDEFS\n **********************\/\n\n\/**********************\n * GLOBAL PROTOTYPES\n **********************\/\n\/* Initialize low level display driver *\/\nvoid lv_port_disp_init(void);\n\n\/* Enable updating the screen (the flushing process) when disp_flush() is called by LVGL\n *\/\nvoid disp_enable_update(void);\n\n\/* Disable updating the screen (the flushing process) when disp_flush() is called by LVGL\n *\/\nvoid disp_disable_update(void);\n\n\/**********************\n *      MACROS\n **********************\/\n\n#ifdef __cplusplus\n} \/*extern &quot;C&quot;*\/\n#endif\n\n#endif \/*LV_PORT_DISP_TEMPL_H*\/\n\n#endif \/*Disable\/Enable content*\/<\/pre><\/div>\n\n\n\n<p><\/p>\n\n\n\n<p>Next, for display_lvgl_port.c source file:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">#if 1\n\n\/*********************\n *      INCLUDES\n *********************\/\n#include &lt;display_lvgl_port.h&gt;\n#include &lt;stdbool.h&gt;\n\n#include &quot;ILI9341.h&quot;\n\n\n#define MY_DISP_HOR_RES    240\n#define MY_DISP_VER_RES    320\n\nstatic uint8_t buf_2_1[240*32*2]__attribute__((aligned(4)));;\nstatic uint8_t buf_2_2[240*32*2]__attribute__((aligned(4)));;\n\nlv_display_t  *disp;\n\n\/*********************\n *      DEFINES\n *********************\/\n\n\/**********************\n *      TYPEDEFS\n **********************\/\n\n\/**********************\n *  STATIC PROTOTYPES\n **********************\/\nstatic void disp_init(void);\n\nstatic void disp_flush(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map);\n\n\/**********************\n *  STATIC VARIABLES\n **********************\/\n\n\/**********************\n *      MACROS\n **********************\/\n\n\/**********************\n *   GLOBAL FUNCTIONS\n **********************\/\n\nvoid lv_port_disp_init(void)\n{\n    \/*-------------------------\n     * Initialize your display\n     * -----------------------*\/\n    disp_init();\n\n    \/*------------------------------------\n     * Create a display and set a flush_cb\n     * -----------------------------------*\/\n     disp = lv_display_create(MY_DISP_HOR_RES, MY_DISP_VER_RES);\n    lv_display_set_flush_cb(disp, disp_flush);\n\n\n    lv_display_set_buffers(disp, buf_2_1, buf_2_2, sizeof(buf_2_1), LV_DISPLAY_RENDER_MODE_PARTIAL);\n    \/\/ Change the color format to RGB565_SWAPPED\n    lv_display_set_color_format(disp, LV_COLOR_FORMAT_RGB565_SWAPPED);\n\n\n\n}\n\n\/**********************\n *   STATIC FUNCTIONS\n **********************\/\n\n\/*Initialize your display and the required peripherals.*\/\nstatic void disp_init(void)\n{\n\tILI9341_Init(ROTATE_0);\n}\n\nvolatile bool disp_flush_enabled = true;\n\n\/* Enable updating the screen (the flushing process) when disp_flush() is called by LVGL\n *\/\nvoid disp_enable_update(void)\n{\n    disp_flush_enabled = true;\n}\n\n\/* Disable updating the screen (the flushing process) when disp_flush() is called by LVGL\n *\/\nvoid disp_disable_update(void)\n{\n    disp_flush_enabled = false;\n}\n\n\/*Flush the content of the internal buffer the specific area on the display.\n *`px_map` contains the rendered image as raw pixel map and it should be copied to `area` on the display.\n *You can use DMA or any hardware acceleration to do this operation in the background but\n *'lv_display_flush_ready()' has to be called when it's finished.*\/\nstatic void disp_flush(lv_display_t * disp_drv, const lv_area_t * area, uint8_t * px_map)\n{\n\tif(disp_flush_enabled)\n\t    {\n\t        \/\/ Simply pass the area coordinates directly to your DMA function\n\t        ILI9341_Draw_Bitmap_DMA(area-&gt;x1, area-&gt;y1, area-&gt;x2, area-&gt;y2, px_map);\n\t    }\n\t    else\n\t    {\n\t        \/\/ If flushing is disabled, we must still inform LVGL to prevent a deadlock\n\t        lv_display_flush_ready(disp_drv);\n\t    }\n\n}\n\nvoid ILI9341_Transfer_Completed(void)\n{\n\tlv_display_flush_ready(disp);\n}\n\n#else \/*Enable this file at the top*\/\n\n\/*This dummy typedef exists purely to silence -Wpedantic.*\/\ntypedef int keep_pedantic_happy;\n#endif\n<\/pre><\/div>\n\n\n\n<p>For touch_lvgl_port.h header file:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">#if 1\n\n#ifndef LV_PORT_INDEV_TEMPL_H\n#define LV_PORT_INDEV_TEMPL_H\n\n#ifdef __cplusplus\nextern &quot;C&quot; {\n#endif\n\n\/*********************\n *      INCLUDES\n *********************\/\n\n#include &quot;lvgl.h&quot;\n\n\n\/*********************\n *      DEFINES\n *********************\/\n\n\/**********************\n *      TYPEDEFS\n **********************\/\n\n\/**********************\n * GLOBAL PROTOTYPES\n **********************\/\nvoid lv_port_indev_init(void);\n\n\/**********************\n *      MACROS\n **********************\/\n\n#ifdef __cplusplus\n} \/*extern &quot;C&quot;*\/\n#endif\n\n#endif \/*LV_PORT_INDEV_TEMPL_H*\/\n\n#endif \/*Disable\/Enable content*\/\n<\/pre><\/div>\n\n\n\n<p>Next, for touch_lvgl_port.c source file:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">#if 1\n\n\/*********************\n *      INCLUDES\n *********************\/\n#include &lt;touch_lvgl_port.h&gt;\n\n#include &quot;STMPE811.h&quot;\n\n\/*********************\n *      DEFINES\n *********************\/\n\n\/**********************\n *      TYPEDEFS\n **********************\/\n\n\/**********************\n *  STATIC PROTOTYPES\n **********************\/\n\nstatic void touchpad_init(void);\nstatic void touchpad_read(lv_indev_t * indev, lv_indev_data_t * data);\nstatic bool touchpad_is_pressed(void);\nstatic void touchpad_get_xy(int32_t * x, int32_t * y);\n\n\n\n\/**********************\n *  STATIC VARIABLES\n **********************\/\nlv_indev_t * indev_touchpad;\n\n\/**********************\n *      MACROS\n **********************\/\n\n\/**********************\n *   GLOBAL FUNCTIONS\n **********************\/\n\nvoid lv_port_indev_init(void)\n{\n    \/**\n     * Here you will find example implementation of input devices supported by LittelvGL:\n     *  - Touchpad\n     *  - Mouse (with cursor support)\n     *  - Keypad (supports GUI usage only with key)\n     *  - Encoder (supports GUI usage only with: left, right, push)\n     *  - Button (external buttons to press points on the screen)\n     *\n     *  The `..._read()` function are only examples.\n     *  You should shape them according to your hardware\n     *\/\n\n    \/*------------------\n     * Touchpad\n     * -----------------*\/\n\n    \/*Initialize your touchpad if you have*\/\n    touchpad_init();\n\n    \/*Register a touchpad input device*\/\n    indev_touchpad = lv_indev_create();\n    lv_indev_set_type(indev_touchpad, LV_INDEV_TYPE_POINTER);\n    lv_indev_set_read_cb(indev_touchpad, touchpad_read);\n\n\n}\n\n\/**********************\n *   STATIC FUNCTIONS\n **********************\/\n\n\/*------------------\n * Touchpad\n * -----------------*\/\n\n\/*Initialize your touchpad*\/\nstatic void touchpad_init(void)\n{\n\tSTMPE811_Touch_Enable();\n}\n\n\/*Will be called by the library to read the touchpad*\/\nstatic void touchpad_read(lv_indev_t * indev_drv, lv_indev_data_t * data)\n{\n    static int32_t last_x = 0;\n    static int32_t last_y = 0;\n\n    \/*Save the pressed coordinates and the state*\/\n    if(touchpad_is_pressed()) {\n        touchpad_get_xy(&amp;last_x, &amp;last_y);\n        data-&gt;state = LV_INDEV_STATE_PRESSED;\n    }\n    else {\n        data-&gt;state = LV_INDEV_STATE_RELEASED;\n    }\n\n    \/*Set the last pressed coordinates*\/\n    data-&gt;point.x = last_x;\n    data-&gt;point.y = last_y;\n}\n\n\/*Return true is the touchpad is pressed*\/\nstatic bool touchpad_is_pressed(void)\n{\n\tif (isTouched()==touched)\n\t{\n\t\treturn true;\n\t}\n\n\telse\n\t{\n\t\treturn false;\n\t}\n}\n\n\/*Get the x and y coordinates if the touchpad is pressed*\/\nstatic void touchpad_get_xy(int32_t * x, int32_t * y)\n{\n\tuint16_t X,Y;\n\tgetTouchValue(&amp;X, &amp;Y);\n\n\t*x=X;\n\t*y=320-Y;\n\n\n}\n\n#endif<\/pre><\/div>\n\n\n\n<p>Thats all for the porting process.<\/p>\n\n\n\n<p>Next, open main.c file.<\/p>\n\n\n\n<p>We start by including the following header files:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">#include &quot;lvgl.h&quot;\n#include &lt;display_lvgl_port.h&gt;\n#include &lt;touch_lvgl_port.h&gt;\n#include &quot;demos\/widgets\/lv_demo_widgets.h&quot;<\/pre><\/div>\n\n\n\n<p><\/p>\n\n\n\n<p>Next, in main function.<\/p>\n\n\n\n<p>First, initialize lvgl:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">lv_init();<\/pre><\/div>\n\n\n\n<p>attach <strong>HAL_GetTick<\/strong> to <strong>lv_tick_set_cb<\/strong> as follows:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">lv_tick_set_cb(HAL_GetTick);<\/pre><\/div>\n\n\n\n<p>Intialize the display and touch as follows:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">lv_port_disp_init();\nlv_port_indev_init();<\/pre><\/div>\n\n\n\n<p>In while 1 loop:<\/p>\n\n\n\n<p>Call lv_timer_handler each 5ms as follows<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">lv_timer_handler();\nHAL_Delay(5);<\/pre><\/div>\n\n\n\n<p>Save, build the project and run it as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"34\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-1024x34.png\" alt=\"\" class=\"wp-image-4349\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-1024x34.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-300x10.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-768x26.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-1536x51.png 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-1150x38.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-750x25.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-400x13.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-250x8.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1.png 1986w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>You may download the code from this <a href=\"https:\/\/github.com\/hussamaldean\/Embedded-Expert-Post-Projects\/tree\/main\/Projects\/ILI9341_STM32\" data-type=\"link\" data-id=\"https:\/\/github.com\/hussamaldean\/Embedded-Expert-Post-Projects\/tree\/main\/Projects\/ILI9341_STM32\" target=\"_blank\" rel=\"noreferrer noopener\">github repository.<\/a><\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3. Results:<\/h2>\n\n\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-9-16 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"LvGL V9.5 on STM32F429 Discovery\" width=\"563\" height=\"1000\" src=\"https:\/\/www.youtube.com\/embed\/ftD9bRwJnpI?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Happy coding \ud83d\ude09<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In Part 6, we elevate our project by integrating the\u00a0Light and Versatile Graphics Library (LVGL), transforming raw hardware drivers into a professional-grade graphical user interface. You will learn how to bridge your touch and display drivers with the library&#8217;s HAL and deploy a comprehensive widget demo to showcase the STM32F429\u2019s rendering capabilities. In this guide, [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,19,11,12],"tags":[],"class_list":["post-4394","post","type-post","status-publish","format-standard","hentry","category-embedded-systems","category-lcd","category-peripheral-drivers","category-stm32"],"_links":{"self":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/4394"}],"collection":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4394"}],"version-history":[{"count":2,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/4394\/revisions"}],"predecessor-version":[{"id":4408,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/4394\/revisions\/4408"}],"wp:attachment":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4394"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4394"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4394"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}