How to port uCGUI with STM32?

 Here’s a practical, battle-tested checklist for porting uC/GUI (SEGGER emWin) on STM32. It works whether you use raw SPI LCDs (e.g., ST7735/ILI9341) or LTDC/DMA2D panels on F4/F7/H7.




0) Prereqs

  • Tooling: STM32CubeIDE (or Keil/IAR), CubeMX for clock/pin init.

  • uC/GUI (emWin) package:

    • For many STM32 MCUs, ST ships STemWin (a licensed emWin build).

    • Otherwise, use a SEGGER emWin library matching your compiler/CPU.

  • BSP drivers: Your LCD (SPI/LCD controller or LTDC) + optional touch (I²C/ADC).


1) Add emWin/uC-GUI to the project

  • Include core sources/libs (GUI/, WM/, LCD/, etc.) or prebuilt library from STemWin.

  • Add config stubs (typically):

    • GUIConf.c → memory allocator configuration

    • LCDConf.c (a.k.a. LCD_X_Config) → display driver & layer config

    • GUI_X.c → timing, multitasking hooks (OS or bare-metal)

Project include paths should point to emWin headers; link the right library variant (e.g., Cortex-M4F/Thumb).


2) Allocate GUI memory (GUIConf.c)

uC/GUI needs a heap arena for windows, objects, fonts, etc.

#include "GUI.h" #define GUI_NUMBYTES (120*1024) // tune for your app static U32 GUI_MemPool[GUI_NUMBYTES/4]; void GUI_X_Config(void) { GUI_ALLOC_AssignMemory(GUI_MemPool, sizeof(GUI_MemPool)); GUI_ALLOC_SetAvBlockSize(32); // typical GUI_SetDefaultFont(GUI_FONT_6X8); }
  • Increase GUI_NUMBYTES for complex UIs, antialiased fonts, or multiple layers.


3) Choose the right display driver (LCDConf.c)

uC/GUI talks to your panel via a driver. Two common families:

A) RGB panels via LTDC (F429/F746/H7…)

  • Use GUIDRV_Lin with color conversion like GUICC_M565 (RGB565) or GUICC_M8888I (ARGB8888).

  • Enable LTDC and DMA2D in CubeMX; set timings to your panel.

  • Example:

#include "GUI.h" #include "GUIDRV_Lin.h" #define XSIZE_PHYS 480 #define YSIZE_PHYS 272 #define NUM_BUFFERS 2 static U32 FrameBuf[YSIZE_PHYS][XSIZE_PHYS]; void LCD_X_Config(void) { GUI_DEVICE * pDev = GUI_DEVICE_CreateAndLink(GUIDRV_LIN_16, GUICC_M565, 0, 0); LCD_SetSizeEx (0, XSIZE_PHYS, YSIZE_PHYS); LCD_SetVSizeEx(0, XSIZE_PHYS, YSIZE_PHYS); // Tell emWin where the framebuffer is: LCD_SetVRAMAddrEx(0, (void*)FrameBuf); // Optional: multiple buffers (double-buffering) LCD_SetBufferPtrEx(0, 0, (void*)FrameBuf); }
  • Hook VSYNC/line interrupt to GUI_MULTIBUF_Confirm() if you do true multibuffering.

B) SPI-attached controllers (ILI9341/ST7789/GC9A01…)

  • Use GUIDRV_FlexColor with the proper controller modes.

  • You implement low-level read/write funcs (cmd/data) that call your SPI (preferably with DMA).

  • Example sketch:

#include "GUI.h" #include "GUIDRV_FlexColor.h" static void _WriteReg(U8 Reg) { LCD_Select(); LCD_DC_Command(); SPI_TX(&Reg,1); LCD_Unselect(); } static void _WriteData8(U8 Data) { LCD_Select(); LCD_DC_Data(); SPI_TX(&Data,1); LCD_Unselect(); } static void _WriteDataM8(U8*p, int n){ LCD_Select(); LCD_DC_Data(); SPI_TX(p,n); LCD_Unselect(); } void LCD_X_Config(void) { GUI_DEVICE * pDev = GUI_DEVICE_CreateAndLink(GUIDRV_FLEXCOLOR, GUICC_M565, 0, 0); GUIDRV_FlexColor_SetFunc(pDev, &GUIDRV_FlexColor_F66709, // pick controller func set GUIDRV_FLEXCOLOR_M16C0B8); // 16-bit, type depends on wiring LCD_SetSizeEx(0, 240, 320); LCD_SetVSizeEx(0, 240, 320); // Bind your low-level interface GUI_PORT_API PortAPI = {0}; PortAPI.pfWrite8_A0 = _WriteReg; PortAPI.pfWrite8_A1 = _WriteData8; PortAPI.pfWriteM8_A1 = _WriteDataM8; GUIDRV_FlexColor_SetReadFunc(pDev, NULL); GUI_DEVICE_SetPortAPI(pDev, &PortAPI, 0); }

Adjust controller variant and mode to your panel; init sequence (sleep-out, pixel format, MADCTL) belongs in your LCD init driver before GUI starts.


4) OS interface & timing (GUI_X.c)

  • Bare-metal: provide GUI_X_GetTime() (millis) and GUI_X_Delay(ms) using SysTick.

  • With RTOS (FreeRTOS/uC/OS-II): implement mutex/lock wrappers so emWin can be called from different tasks; call GUI_Delay() or run a GUI task.

Bare-metal skeleton:

int GUI_X_GetTime(void) { return HAL_GetTick(); } void GUI_X_Delay(int ms){ HAL_Delay(ms); } void GUI_X_Unlock(void) {} void GUI_X_Lock(void) {} U32 GUI_X_GetTaskId(void){ return 0; }

FreeRTOS idea:

void GUI_X_Lock(void) { xSemaphoreTake(guiMutex, portMAX_DELAY); } void GUI_X_Unlock(void) { xSemaphoreGive(guiMutex); } U32 GUI_X_GetTaskId(void){ return (U32)xTaskGetCurrentTaskHandle(); }

5) Initialize in main()

  • Bring up clocks, GPIO, SPI/LTDC, DMA, touch, etc.

  • Init GUI last, after display/touch are ready.

int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_DMA_Init(); MX_SPI1_Init(); // or MX_LTDC_Init(); MX_DMA2D_Init(); LCD_Controller_Init(); // your panel init GUI_Init(); GUI_SetBkColor(GUI_BLACK); GUI_Clear(); GUI_SetColor(GUI_WHITE); GUI_DispStringAt("Hello, emWin!", 10, 10); while (1) { GUI_Exec(); // process windows/timers GUI_Delay(10); } }

6) Touch input (optional)

  • For resistive: read ADC X/Y, feed to emWin via GUI_TOUCH_StoreStateEx(&State).

  • For capacitive (FT6206/GT911 via I²C): read coordinates/press state periodically or in IRQ, then:

GUI_PID_STATE s; s.x = tp_x; s.y = tp_y; s.Pressed = tp_pressed; s.Layer = 0; GUI_TOUCH_StoreStateEx(&s);
  • Run GUI_TOUCH_Calibrate() or map raw → screen coordinates.


7) Fonts, languages, AA

  • Include the fonts you need (ASCII/Unicode). Antialiasing needs more GUI_NUMBYTES.

  • For Chinese or mixed locales, consider SIF/TTF support and external font caches.


8) Performance tips

  • LTDC/DMA2D: enable and let emWin use it for fills/blits; huge speedup.

  • SPI panels: use DMA + largest possible bursts; keep command/data switches minimal; consider parallel 8080 if bandwidth is tight.

  • Caches (F7/H7): if framebuffer in D-Cacheable SRAM/SDRAM, do cache maintenance (clean/invalidate) or mark region non-cacheable (MPU).

  • Use multiple buffers (double/triple) to avoid tearing; sync to VSYNC/line event.


9) Common pitfalls (quick fix list)

  • Wrong color order (BGR vs RGB) → scrambled colors → fix MADCTL / color conversion (GUICC_M565 vs GUICC_565).

  • Forgetting GUI_Exec() / GUI_Delay() in the main/GUI task → widgets don’t update.

  • Too small GUI_NUMBYTES → random GUI alloc failures; increase pool size.

  • SPI too slow for full-screen redraw → enable DMA, partial redraws, or use LTDC panels.

  • Touch coords swapped/inverted → unify with transform or correct controller settings.

  • Interrupt priorities vs. HAL DMA callbacks causing artifacts → review NVIC priorities for LCD/Touch/DMA2D.


10) Minimal “it works” checklist

  1. Panel initializes (backlight on, test pattern OK).

  2. GUI_Init() returns; GUI_DispString() shows text at expected position.

  3. Touch events move a demo widget (e.g., GUIDEMO_Start() if available).

  4. No tearing/flicker during simple animations.

评论

此博客中的热门博文

Detailed Explanation of STM32 HAL Library Clock System

How To Connect Stm32 To PC?

How do you set up ADC (Analog-to-Digital Converter) in STM32?