{"id":526,"date":"2015-08-17T17:26:05","date_gmt":"2015-08-18T00:26:05","guid":{"rendered":"http:\/\/visualgdb.com\/w\/?p=526"},"modified":"2015-08-17T17:39:16","modified_gmt":"2015-08-18T00:39:16","slug":"developing-an-lcd-application-for-stm32f7-discovery-with-visualgdb-and-openocd","status":"publish","type":"post","link":"https:\/\/visualgdb.com\/tutorials\/arm\/stm32\/lcd\/","title":{"rendered":"Developing an LCD application for the STM32F7-Discovery with VisualGDB and OpenOCD"},"content":{"rendered":"<p>This tutorial shows how to create an application that will display a basic animation on the LCD screen of the STM32F7-Discovery board. We will show how to:<\/p>\n<ul>\n<li>Access the LCD controller framebuffer<\/li>\n<li>Access the DRAM memory<\/li>\n<li>Embed binary resources in your firmware<\/li>\n<li>Build and debug STM32F7 firmware with OpenOCD and Visual Studio<\/li>\n<\/ul>\n<p>Before you begin, install VisualGDB 5.0 or later and follow our <a href=\"http:\/\/visualgdb.com\/tutorials\/arm\/stm32\/stm32f7\">basic STM32F7 tutorial<\/a> to ensure that your board can be programmed.<\/p>\n<ol>\n<li>Start Visual Studio and open VisualGDB Embedded Project wizard:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/01-prj.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-527\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/01-prj.png\" alt=\"01-prj\" width=\"800\" height=\"450\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/01-prj.png 800w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/01-prj-300x169.png 300w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/a><\/li>\n<li>Proceed with creating a new project:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/02-defaults.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-528\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/02-defaults.png\" alt=\"02-defaults\" width=\"702\" height=\"571\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/02-defaults.png 702w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/02-defaults-300x244.png 300w\" sizes=\"(max-width: 702px) 100vw, 702px\" \/><\/a><\/li>\n<li>Select the ARM toolchain and pick your device from the list. To save space, select newlib-nano and ensure that both checkboxes below it are set:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/03-device6.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-529\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/03-device6.png\" alt=\"03-device\" width=\"702\" height=\"609\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/03-device6.png 702w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/03-device6-300x260.png 300w\" sizes=\"(max-width: 702px) 100vw, 702px\" \/><\/a><\/li>\n<li>On the next page select the LCD Demo sample:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/04-lcddemo.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-530\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/04-lcddemo.png\" alt=\"04-lcddemo\" width=\"702\" height=\"609\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/04-lcddemo.png 702w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/04-lcddemo-300x260.png 300w\" sizes=\"(max-width: 702px) 100vw, 702px\" \/><\/a><\/li>\n<li>On the last page of the wizard select the debug method you want to use. We will use OpenOCD with the on-board ST-Link interface:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/05-debug4.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-531\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/05-debug4.png\" alt=\"05-debug\" width=\"702\" height=\"609\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/05-debug4.png 702w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/05-debug4-300x260.png 300w\" sizes=\"(max-width: 702px) 100vw, 702px\" \/><\/a><\/li>\n<li>Press &#8220;Finish&#8221; to create your project and build it using the Build Solution command:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/06-build3.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-532\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/06-build3.png\" alt=\"06-build\" width=\"695\" height=\"606\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/06-build3.png 695w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/06-build3-300x262.png 300w\" sizes=\"(max-width: 695px) 100vw, 695px\" \/><\/a><\/li>\n<li>Press F5 to start debugging. VisualGDB will program the FLASH memory and start your program. The display will show a static image:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/08-gears.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-533\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/08-gears.jpg\" alt=\"08-gears\" width=\"700\" height=\"437\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/08-gears.jpg 700w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/08-gears-300x187.jpg 300w\" sizes=\"(max-width: 700px) 100vw, 700px\" \/><\/a><\/li>\n<li>Now we will modify the program to show something dynamic. Go to the definition of the BSP_LCD_LayerRgb565Init() function that is responsible for initializing the LCD controller:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/07-layer.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-534\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/07-layer.png\" alt=\"07-layer\" width=\"695\" height=\"606\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/07-layer.png 695w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/07-layer-300x262.png 300w\" sizes=\"(max-width: 695px) 100vw, 695px\" \/><\/a><\/li>\n<li>The framebuffer address is stored in the FBStartAddress field of the layer configuration structure:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/08-addr.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-535\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/08-addr.png\" alt=\"08-addr\" width=\"695\" height=\"605\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/08-addr.png 695w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/08-addr-300x261.png 300w\" sizes=\"(max-width: 695px) 100vw, 695px\" \/><\/a><\/li>\n<li>Use the Code Map to display the functions accessing the field and the functions calling them. This should give a good overview of the LCD setup functions. In particular, we will be using the HAL_LDTC_SetAddress() function to change the address of the frame buffer:\u00a0 <a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/09-map.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-537\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/09-map.png\" alt=\"09-map\" width=\"695\" height=\"605\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/09-map.png 695w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/09-map-300x261.png 300w\" sizes=\"(max-width: 695px) 100vw, 695px\" \/><\/a><\/li>\n<li>You can also use the CodeMap to verify that BSP_LCD_Init() actually initializes the DRAM controller: <a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/10-sdram.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-538\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/10-sdram.png\" alt=\"10-sdram\" width=\"938\" height=\"283\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/10-sdram.png 938w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/10-sdram-300x91.png 300w\" sizes=\"(max-width: 938px) 100vw, 938px\" \/><\/a><\/li>\n<li>Before we can place data into DRAM, we will make a very basic allocator with no free() function that will allocate DRAM memory dynamically so that we don&#8217;t need to handle it at compile time:\n<pre class=\"\">#include &lt;string.h&gt;\r\n#include &lt;algorithm&gt;\r\n\r\nclass OneWayAllocator\r\n{\r\nprivate:\r\n\u00a0\u00a0 \u00a0char *m_pData;\r\n\u00a0\u00a0 \u00a0int m_RemainingSize;\r\n\r\npublic:\r\n\u00a0\u00a0 \u00a0OneWayAllocator(void *pData, int remainingSize)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0: m_pData((char *)pData)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0, m_RemainingSize(remainingSize)\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0void *Allocate(size_t size)\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0if (size &gt;= m_RemainingSize)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0asm(\"bkpt 255\");\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0return NULL;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0void *pResult = m_pData;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0m_RemainingSize -= size;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0m_pData += size;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0return pResult;\r\n\u00a0\u00a0 \u00a0}\r\n};\r\n\r\nOneWayAllocator g_DRAMAllocator((void *)LCD_FB_START_ADDRESS, 8 * 1024 * 1024);<\/pre>\n<p>The LCD_FB_START_ADDRESS constant is equal to 0xC0000000 and it specifies the DRAM address.<\/li>\n<li>Now we can add a double-buffered screen object that will allocate 2 buffers and use one of them as the framebuffer while the other one is used to draw the next frame:\n<pre class=\"\">class DoubleBufferedScreen\r\n{\r\nprivate:\r\n\u00a0\u00a0 \u00a0int m_Width, m_Height;\r\n\u00a0\u00a0 \u00a0uint16_t *m_pBuffers[2];\r\n\u00a0\u00a0 \u00a0uint16_t *m_pBackground;\r\n\u00a0\u00a0 \u00a0int m_ActiveBuffer;\r\n\u00a0\u00a0 \u00a0\r\npublic:\r\n\u00a0\u00a0 \u00a0DoubleBufferedScreen(int width, int height, uint16_t *pBackground)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0: m_Width(width)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0, m_Height(height)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0, m_pBackground(pBackground)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0, m_ActiveBuffer(0)\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0m_pBuffers[0] = (uint16_t *)g_DRAMAllocator.Allocate(width * height * sizeof(m_pBuffers[0][0]));\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0m_pBuffers[1] = (uint16_t *)g_DRAMAllocator.Allocate(width * height * sizeof(m_pBuffers[0][0]));\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0memcpy(m_pBuffers[0], m_pBackground, m_Width * m_Height * sizeof(m_pBuffers[0][0]));\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0memcpy(m_pBuffers[1], m_pBackground, m_Width * m_Height * sizeof(m_pBuffers[0][0]));\r\n\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0uint16_t *GetBackBuffer()\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0return m_pBuffers[!m_ActiveBuffer];\r\n\u00a0\u00a0 \u00a0}\r\n\r\n\u00a0\u00a0 \u00a0void SwitchBuffer()\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0m_ActiveBuffer = !m_ActiveBuffer;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0BSP_LCD_SetLayerAddress(0, (uint32_t)m_pBuffers[m_ActiveBuffer]);\r\n\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0int GetWidth()\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0return m_Width;\r\n\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0int GetHeight()\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0return m_Height;\r\n\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0int GetBackBufferIndex()\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0return !m_ActiveBuffer;\r\n\u00a0\u00a0 \u00a0}\r\n};<\/pre>\n<\/li>\n<li>Initially both buffers will just contain the background picture and we will not draw anything extra:<\/li>\n<li>\n<pre class=\"\">\u00a0\u00a0\u00a0 DoubleBufferedScreen screen(480, 272, (uint16_t *)s_Logo);\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0for (;;)\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0screen.SwitchBuffer();\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0HAL_Delay(10);\r\n\u00a0\u00a0 \u00a0}<\/pre>\n<\/li>\n<li>Compile your program, start it and verify that the background picture is shown like before. Then use the debugger to verify that the layer 1 of the LCD controller is using the currently active screen for the framebuffer:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/11-screens.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-539\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/11-screens.png\" alt=\"11-screens\" width=\"695\" height=\"606\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/11-screens.png 695w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/11-screens-300x262.png 300w\" sizes=\"(max-width: 695px) 100vw, 695px\" \/><\/a><\/li>\n<li>Now we will add a widget class. The widget will have 2 main methods:\n<ul>\n<li>Draw itself on a screen<\/li>\n<li>Restore the background in the area occupied by itself<\/li>\n<\/ul>\n<p>This will ensure that we won&#8217;t need to copy the entire image background each time:<\/p>\n<pre class=\"\">void DrawPixels(uint16_t *pDst, const uint16_t *pSrc, int pixels)\r\n{\r\n\u00a0\u00a0 \u00a0for (int i = 0; i &lt; pixels; i++)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0pDst[i] = pSrc[i];\r\n}\r\n\r\nclass Widget\r\n{\r\nprivate:\r\n\u00a0\u00a0 \u00a0int m_Width, m_Height;\r\n\u00a0\u00a0 \u00a0uint16_t *m_pData;\r\n\u00a0\u00a0 \u00a0DoubleBufferedScreen *m_pScreen;\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0int m_X, m_Y;\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0int m_BufferedX[2], m_BufferedY[2];\r\npublic:\r\n\u00a0\u00a0 \u00a0Widget(DoubleBufferedScreen *pScreen, int width, int height, uint16_t *pData)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0: m_Width(width)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0, m_Height(height)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0, m_pData(pData)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0, m_X(0)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0, m_Y(0)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0, m_pScreen(pScreen)\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0m_BufferedX[0] = m_BufferedX[1] = m_X;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0m_BufferedY[0] = m_BufferedY[1] = m_Y;\r\n\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0~Widget()\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0void Draw()\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0if (m_X &gt; m_pScreen-&gt;GetWidth() || m_Y &gt; m_pScreen-&gt;GetHeight())\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0return;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0int todoX = std::min(m_pScreen-&gt;GetWidth() - m_X, m_Width);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0int todoY = std::min(m_pScreen-&gt;GetHeight() - m_Y, m_Height);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0for (int y = 0; y &lt; todoY; y++)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0DrawPixels(m_pScreen-&gt;GetBackBuffer() + (y + m_Y) * m_pScreen-&gt;GetWidth() + m_X, m_pData + y * m_Width, todoX);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0int bufIndex = m_pScreen-&gt;GetBackBufferIndex();\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0m_BufferedX[bufIndex] = m_X;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0m_BufferedY[bufIndex] = m_Y;\r\n\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0void RestoreBackground(uint16_t *pBackground)\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0int bufIndex = m_pScreen-&gt;GetBackBufferIndex();\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0int myX = m_BufferedX[bufIndex], myY = m_BufferedY[bufIndex];\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0if (myX &gt; m_pScreen-&gt;GetWidth() || myY &gt; m_pScreen-&gt;GetHeight())\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0return;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0int todoX = std::min(m_pScreen-&gt;GetWidth() - myX, m_Width);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0int todoY = std::min(m_pScreen-&gt;GetHeight() - myY, m_Height);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0for (int y = 0; y &lt; todoY; y++)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0DrawPixels(m_pScreen-&gt;GetBackBuffer() + (y + myY) * m_pScreen-&gt;GetWidth() + myX, pBackground + (y + myY) * m_pScreen-&gt;GetWidth() + myX, todoX);\r\n\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0int GetX()\u00a0\u00a0 \u00a0{ return m_X; }\r\n\u00a0\u00a0 \u00a0int GetY()\u00a0\u00a0 \u00a0{ return m_Y; }\r\n\u00a0\u00a0 \u00a0int GetWidth()\u00a0\u00a0 \u00a0{ return m_Width; }\r\n\u00a0\u00a0 \u00a0int GetHeight()\u00a0\u00a0 \u00a0{ return m_Height; }\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0void Move(int deltaX, int deltaY)\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0m_X = std::max(m_X + deltaX, 0);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0m_Y = std::max(m_Y + deltaY, 0);\r\n\u00a0\u00a0 \u00a0}\r\n};<\/pre>\n<p>Note that the DRAM controller will generate a memory fault in case of an unaligned 32-bit access, so we need to use a function that uses 16-bit accesses. It can be further optimized by using 32-bit accesses after doing 0 or 1 initial 16-bit accesses.<\/li>\n<li>We will also add a class responsible for moving the widgets:\n<pre class=\"\">class WidgetMover\r\n{\r\nprivate:\r\n\u00a0\u00a0 \u00a0Widget *m_pWidget;\r\n\u00a0\u00a0 \u00a0DoubleBufferedScreen *m_pScreen;\r\n\u00a0\u00a0 \u00a0int m_XSpeed, m_YSpeed;\r\n\u00a0\u00a0 \u00a0\r\npublic:\r\n\u00a0\u00a0 \u00a0WidgetMover(DoubleBufferedScreen *pScreen, Widget *pWidget, int xSpeed, int ySpeed)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0: m_pWidget(pWidget)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0, m_pScreen(pScreen)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0, m_XSpeed(xSpeed)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0, m_YSpeed(ySpeed)\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0void Step()\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0if (m_pWidget-&gt;GetX() &lt;= 0 &amp;&amp; m_XSpeed &lt; 0)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0m_XSpeed = -m_XSpeed;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0if (m_pWidget-&gt;GetY() &lt;= 0 &amp;&amp; m_YSpeed &lt; 0)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0m_YSpeed = -m_YSpeed;\r\n\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0if (m_pWidget-&gt;GetX() + m_pWidget-&gt;GetWidth() &gt;= m_pScreen-&gt;GetWidth() &amp;&amp; m_XSpeed &gt; 0)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0m_XSpeed = -m_XSpeed;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0if (m_pWidget-&gt;GetY() + m_pWidget-&gt;GetHeight() &gt;= m_pScreen-&gt;GetHeight() &amp;&amp; m_YSpeed &gt; 0)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0m_YSpeed = -m_YSpeed;\r\n\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0m_pWidget-&gt;Move(m_XSpeed, m_YSpeed);\r\n\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0Widget *GetWidget()\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0return m_pWidget;\r\n\u00a0\u00a0 \u00a0}\r\n};<\/pre>\n<\/li>\n<li>Finally, add code to main() that will draw a moving black square bouncing off the screen edges:\n<pre class=\"\">\u00a0\u00a0\u00a0 const int WidgetSize = 48;\r\n\u00a0\u00a0 \u00a0DoubleBufferedScreen screen(480, 272, (uint16_t *)s_Logo);\r\n\u00a0\u00a0 \u00a0uint16_t *pSquare = (uint16_t*)g_DRAMAllocator.Allocate(WidgetSize * WidgetSize * sizeof(uint16_t));\r\n\u00a0\u00a0 \u00a0memset(pSquare, 0, WidgetSize * WidgetSize * sizeof(uint16_t));\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0Widget widget(&amp;screen, WidgetSize, WidgetSize, pSquare);\r\n\u00a0\u00a0 \u00a0WidgetMover mover(&amp;screen, &amp;widget, 1, 1);\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0for (;;)\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0screen.SwitchBuffer();\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0widget.RestoreBackground((uint16_t*)s_Logo);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0mover.Step();\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0widget.Draw();\r\n\u00a0\u00a0 \u00a0}<\/pre>\n<\/li>\n<li>Run your program and observe the moving square:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/12-black.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-540\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/12-black.jpg\" alt=\"12-black\" width=\"700\" height=\"517\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/12-black.jpg 700w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/12-black-300x222.jpg 300w\" sizes=\"(max-width: 700px) 100vw, 700px\" \/><\/a><\/li>\n<li>You may notice some flickering that happens because we start modifying a buffer without waiting for the LCD controller to finish the previous frame and actually switch the frame buffer. To wait for the end of frame add the following line at the end of SwitchBuffer():\n<pre class=\"\">\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0 while (!(LTDC-&gt;CDSR &amp; LTDC_CDSR_VSYNCS)) {}<\/pre>\n<\/li>\n<li>Now we will replace the black square with a transparency-enabled bitmap from an image file. We will display the following image:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/48x48.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-541\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/48x48.png\" alt=\"48x48\" width=\"48\" height=\"48\" \/><\/a>However, instead of decompressing the PNG format on the device, we will use the following C# program to convert it into a raw ARGB format:\n<pre class=\"\">using System.IO;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing System.Threading.Tasks;\r\n\r\nnamespace ImageConverter\r\n{\r\n\u00a0\u00a0\u00a0 class Program\r\n\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 static void Main(string[] args)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Bitmap bmp = new Bitmap(args[0]);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 List&lt;byte&gt; data = new List&lt;byte&gt;();\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 for (int y = 0; y &lt; bmp.Height; y++)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 for (int x = 0; x &lt; bmp.Width; x++)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Color pixel = bmp.GetPixel(x, y);\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 data.Add(pixel.A);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 data.Add(pixel.R);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 data.Add(pixel.G);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 data.Add(pixel.B);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 File.WriteAllBytes(Path.ChangeExtension(args[0], \".dat\"), data.ToArray());\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 }\r\n}<\/pre>\n<\/li>\n<li>Run the program on the image file (or download its output <a href=\"http:\/\/sysprogs.com\/files\/visualgdb\/tutorials\/48x48.dat\">here<\/a>) and place the output file into your project directory. Now we need to include it into the ELF file. Add the following rule to your Makefile right after the .cxx rule:\n<pre class=\"\">$(BINARYDIR)\/%.o : %.dat\r\n\u00a0\u00a0 \u00a0$(TOOLCHAIN_ROOT)\/bin\/arm-eabi-ld -r -b binary $&lt; -o $@<\/pre>\n<p>Then update the source_objs assignment to include .dat files:<\/p>\n<pre class=\"\">source_obj1 := $(all_source_files:.cpp=.o)\r\nsource_obj2 := $(source_obj1:.c=.o)\r\nsource_obj3 := $(source_obj2:.s=.o)\r\nsource_obj4 := $(source_obj3:.S=.o)\r\nsource_obj5 := $(source_obj4:.cc=.o)\r\nsource_obj6 := $(source_obj5:.cxx=.o)\r\nsource_objs := $(source_obj6:.dat=.o)<\/pre>\n<\/li>\n<li>Then add the .dat file to the project same way as you add source files (you can put it into a separate folder in Solution Explorer) and build the project:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/13-binfile1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-543\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/13-binfile1.png\" alt=\"13-binfile\" width=\"695\" height=\"638\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/13-binfile1.png 695w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/13-binfile1-300x275.png 300w\" sizes=\"(max-width: 695px) 100vw, 695px\" \/><\/a>By modifying the Makefile we have created a rule template that will run the ld.exe on each .dat file included into the project converting it onto an object file. By adding the .dat file to the Solution Explorer we instructed VisualGDB to include it in the source file list (VisualGDB checks the rules in the Makefile to determine source extensions). Each object file created from a binary file will contain a symbol called <strong>_binary_&lt;file name&gt;_start<\/strong> allowing to reference it from your code.<\/li>\n<li>Now we will a function for drawing images with transparency:\n<pre class=\"\">void DrawWithTransparency(uint16_t *pDst, const uint32_t *pSrc, int pixels)\r\n{\r\n\u00a0\u00a0 \u00a0for (int i = 0; i &lt; pixels; i++)\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0uint16_t oldPixel = pDst[i];\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0uint32_t value = pSrc[i];\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0int oldR = ((oldPixel &gt;&gt; (5 + 6)) &amp; 0x1F) &lt;&lt; 3;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0int oldG = ((oldPixel &gt;&gt; 5) &amp; 0x3F) &lt;&lt; 2;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0int oldB = (oldPixel &amp; 0x1F) &lt;&lt; 3;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0int picWeight = value &amp; 0xFF;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0int oldWeight = 256 - picWeight;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0int picR = (value &gt;&gt; 8) &amp; 0xFF;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0int picG = (value &gt;&gt; 16) &amp; 0xFF;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0int picB = (value &gt;&gt; 24) &amp; 0xFF;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0int newR = (oldR * oldWeight + picR * picWeight) \/ 256;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0int newG = (oldG * oldWeight + picG * picWeight) \/ 256;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0int newB = (oldB * oldWeight + picB * picWeight) \/ 256;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0newR &gt;&gt;= 3;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0newG &gt;&gt;= 2;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0newB &gt;&gt;= 3;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0uint16_t rgb565 = (ushort)(newB | (newG &lt;&lt; 5) | (newR &lt;&lt; (5 + 6)));\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0pDst[i] = rgb565;\r\n\u00a0\u00a0 \u00a0}\r\n}<\/pre>\n<\/li>\n<li>We will call it instead of DrawPixels() from the Widget::Draw() method. Additionally, we will change the type of Widget::m_pData and the related constructor parameter to int32_t:\n<pre class=\"\">class Widget\r\n{\r\n    \/\/...\r\n\u00a0\u00a0 \u00a0uint32_t *m_pData;\r\n\u00a0\u00a0 \u00a0\/\/...\r\n\u00a0\u00a0 \u00a0Widget(DoubleBufferedScreen *pScreen, int width, int height, uint32_t *pData)\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\/\/...\r\n\u00a0\u00a0 \u00a0}\r\n\r\n\u00a0\u00a0 \u00a0\/\/...\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0void Draw()\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\/\/...\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0for (int y = 0; y &lt; todoY; y++)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0DrawWithTransparency(m_pScreen-&gt;GetBackBuffer() + (y + m_Y) * m_pScreen-&gt;GetWidth() + m_X, m_pData + y * m_Width, todoX);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\/\/...\r\n\u00a0\u00a0 \u00a0}\r\n}<\/pre>\n<\/li>\n<li>Finally import the symbol produced when converting the .dat file in main() and pass its address to the constructor of the widget:\n<pre class=\"\">\u00a0\u00a0\u00a0 extern uint32_t _binary_48x48_dat_start[];\r\n\u00a0\u00a0 \u00a0Widget widget(&amp;screen, WidgetSize, WidgetSize, _binary_48x48_dat_start);<\/pre>\n<\/li>\n<li>Run your program and observe the moving gear icon:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/14-gear.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-544\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/14-gear.jpg\" alt=\"14-gear\" width=\"700\" height=\"468\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/14-gear.jpg 700w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/14-gear-300x201.jpg 300w\" sizes=\"(max-width: 700px) 100vw, 700px\" \/><\/a><\/li>\n<li>The code can be easily scaled to create several gear widgets each with its own speed:\n<pre class=\"\">\u00a0\u00a0\u00a0 std::vector&lt;WidgetMover *&gt; widgets;\r\n\u00a0\u00a0 \u00a0for (int i = 0; i &lt; 7; i++)\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0Widget *pWidget = new Widget(&amp;screen, 48, 48, _binary_48x48_dat_start);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0pWidget-&gt;Move(rand() % screen.GetWidth(), rand() % screen.GetHeight());\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0widgets.push_back(new WidgetMover(&amp;screen, pWidget, i + 1, i + 1));\r\n\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0for (;;)\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0for (auto pWidget : widgets)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0pWidget-&gt;GetWidget()-&gt;RestoreBackground((uint16_t *)s_Logo);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0for (auto pWidget : widgets)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0pWidget-&gt;Step();\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0pWidget-&gt;GetWidget()-&gt;Draw();\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0screen.SwitchBuffer();\r\n\u00a0\u00a0 \u00a0}<\/pre>\n<\/li>\n<li>Note that you will need to enable C++11 in Makefile Settings to use the range-based for loop:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/15-c-11.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-545\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/15-c-11.png\" alt=\"15-c++11\" width=\"804\" height=\"743\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/15-c-11.png 804w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/15-c-11-300x277.png 300w\" sizes=\"(max-width: 804px) 100vw, 804px\" \/><\/a><\/li>\n<li>Build the new program and start it. Observe the 7 independently moving gear widgets:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/gears.gif\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-547\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/gears.gif\" alt=\"gears\" width=\"480\" height=\"360\" \/><\/a>You can download the final version of the main .cpp file <a href=\"http:\/\/sysprogs.com\/files\/visualgdb\/tutorials\/gears.cpp\">here<\/a>.<\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>This tutorial shows how to create an application that will display a basic animation on the LCD screen of the<\/p>\n","protected":false},"author":1,"featured_media":548,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[16],"tags":[53,99,61],"_links":{"self":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/526"}],"collection":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/comments?post=526"}],"version-history":[{"count":5,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/526\/revisions"}],"predecessor-version":[{"id":553,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/526\/revisions\/553"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/media\/548"}],"wp:attachment":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/media?parent=526"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/categories?post=526"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/tags?post=526"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}