Developing an LCD application for the STM32F7-Discovery with VisualGDB and OpenOCD

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:

  • Access the LCD controller framebuffer
  • Access the DRAM memory
  • Embed binary resources in your firmware
  • Build and debug STM32F7 firmware with OpenOCD and Visual Studio

Before you begin, install VisualGDB 5.0 or later and follow our basic STM32F7 tutorial to ensure that your board can be programmed.

  1. Start Visual Studio and open VisualGDB Embedded Project wizard:01-prj
  2. Proceed with creating a new project:02-defaults
  3. 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:03-device
  4. On the next page select the LCD Demo sample:04-lcddemo
  5. 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:05-debug
  6. Press “Finish” to create your project and build it using the Build Solution command:06-build
  7. Press F5 to start debugging. VisualGDB will program the FLASH memory and start your program. The display will show a static image:08-gears
  8. 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:07-layer
  9. The framebuffer address is stored in the FBStartAddress field of the layer configuration structure:08-addr
  10. 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:  09-map
  11. You can also use the CodeMap to verify that BSP_LCD_Init() actually initializes the DRAM controller: 10-sdram
  12. 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’t need to handle it at compile time:

    The LCD_FB_START_ADDRESS constant is equal to 0xC0000000 and it specifies the DRAM address.
  13. 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:
  14. Initially both buffers will just contain the background picture and we will not draw anything extra:
  15. 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:11-screens
  16. Now we will add a widget class. The widget will have 2 main methods:
    • Draw itself on a screen
    • Restore the background in the area occupied by itself

    This will ensure that we won’t need to copy the entire image background each time:


    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.
  17. We will also add a class responsible for moving the widgets:
  18. Finally, add code to main() that will draw a moving black square bouncing off the screen edges:
  19. Run your program and observe the moving square:12-black
  20. 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():
  21. Now we will replace the black square with a transparency-enabled bitmap from an image file. We will display the following image:48x48However, instead of decompressing the PNG format on the device, we will use the following C# program to convert it into a raw ARGB format:
  22. Run the program on the image file (or download its output here) 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:

    Then update the source_objs assignment to include .dat files:
  23. 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:13-binfileBy 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 _binary_<file name>_start allowing to reference it from your code.
  24. Now we will a function for drawing images with transparency:
  25. 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:
  26. Finally import the symbol produced when converting the .dat file in main() and pass its address to the constructor of the widget:
  27. Run your program and observe the moving gear icon:14-gear
  28. The code can be easily scaled to create several gear widgets each with its own speed:
  29. Note that you will need to enable C++11 in Makefile Settings to use the range-based for loop:15-c++11
  30. Build the new program and start it. Observe the 7 independently moving gear widgets:gearsYou can download the final version of the main .cpp file here.