1. 项目概述
在嵌入式开发领域,STM32系列MCU因其出色的性能和丰富的外设资源而广受欢迎。本文将详细介绍如何在ALIENTEK阿波罗STM32F429开发板上,基于openvela实时操作系统运行LVGL图形库的演示程序。这个项目对于想要在嵌入式设备上实现图形界面的开发者来说具有很高的参考价值。
阿波罗STM32F429开发板搭载了STM32F429IGT6微控制器,主频高达180MHz,内置2MB Flash和256KB SRAM,并配备了丰富的接口资源。本项目中使用的RGBLCD模块型号为ATK-7016(7寸,1024*600分辨率),采用GT911电容触控方案。
2. 准备工作
2.1 硬件准备
在开始项目前,需要准备以下硬件设备:
- ALIENTEK阿波罗STM32F429开发板
- ATK-7016 RGBLCD模块
- USB转串口调试工具
- ST-Link调试器或J-Link调试器
- 必要的连接线和电源
2.2 软件准备
需要下载和安装以下软件工具:
- openvela开发环境(建议使用dev分支代码)
- ARM GCC工具链
- STM32CubeProgrammer或OpenOCD
- 串口终端工具(如Putty、Tera Term等)
注意:由于trunk分支的代码中未给STM32/STM32H7开启双缓冲功能,建议下载dev分支的代码。虽然dev分支中的代码也未给STM32开启双缓冲,但我们可以参考STM32H7中的双缓冲实现来移植到STM32上。
3. 开发环境搭建
3.1 源码获取
参考openvela官方文档中的快速入门指南下载最新代码。建议使用git命令克隆仓库:
bash复制git clone -b dev https://gitee.com/open-vela/nuttx.git
3.2 工具链配置
确保系统已安装ARM GCC工具链,并将其添加到系统PATH环境变量中。可以通过以下命令验证工具链是否安装成功:
bash复制arm-none-eabi-gcc --version
3.3 项目目录结构
在nuttx/boards/arm/stm32/目录下,创建一个名为apollo-stm32f429i的新目录,并建立如下子目录结构:
code复制apollo-stm32f429i
├── CMakeLists.txt
├── configs
│ └── lvgl
│ └── defconfig
├── include
│ └── board.h
├── Kconfig
├── scripts
│ ├── ld.script
│ └── Make.defs
└── src
├── CMakeLists.txt
├── Make.defs
├── stm32_appinit.c
├── stm32_autoleds.c
├── stm32_userleds.c
├── stm32_buttons.c
├── stm32_extmem.c
├── stm32_boot.c
├── stm32_bringup.c
├── stm32_touchscreen.c
├── stm32_lcd.c
├── ct_i2c.h
├── ct_i2c.c
├── gt9xx.h
├── gt9xx.c
└── apollo-stm32f429i.h
4. 系统配置与集成
4.1 Kconfig集成
Kconfig用于管理内核和应用的功能配置。需要在多个位置添加配置选项:
- 在
nuttx/boards/Kconfig中定义板级选项:
kconfig复制config ARCH_BOARD_APOLLO_STM32F429I
bool "ATK APOLLO-F429I board"
depends on ARCH_CHIP_STM32F429I
select ARCH_HAVE_LEDS
select ARCH_HAVE_BUTTONS
select ARCH_HAVE_IRQBUTTONS
---help---
ATK Apollo-F429I board based on the STMicro STM32F429IGT6 MCU.
- 设置默认板名:
kconfig复制config ARCH_BOARD
string
default "apollo-stm32f429i" if ARCH_BOARD_APOLLO_STM32F429I
- 加载板级Kconfig文件:
kconfig复制if ARCH_BOARD_APOLLO_STM32F429I
source "boards/arm/stm32/apollo-stm32f429i/Kconfig"
endif
4.2 创建默认配置(defconfig)
创建nuttx/boards/arm/stm32/apollo-stm32f429i/configs/lvgl/defconfig文件,包含基本的系统配置:
config复制CONFIG_APOLLO_STM32F429I_ATK7016=y
CONFIG_APOLLO_STM32F429I_CT_I2C=y
CONFIG_ARCH="arm"
CONFIG_ARCH_BOARD="apollo-stm32f429i"
CONFIG_ARCH_BOARD_APOLLO_STM32F429I=y
CONFIG_ARCH_BUTTONS=y
CONFIG_ARCH_CHIP="stm32"
CONFIG_ARCH_CHIP_STM32=y
CONFIG_ARCH_CHIP_STM32F429I=y
CONFIG_ARCH_STACKDUMP=y
CONFIG_BOARD_LOOPSPERMSEC=13984
CONFIG_BUILTIN=y
CONFIG_DEBUG_CUSTOMOPT=y
CONFIG_DEBUG_SYMBOLS=y
CONFIG_DRIVERS_VIDEO=y
CONFIG_EXAMPLES_FB=y
CONFIG_EXAMPLES_LVGLDEMO=y
CONFIG_FS_PROCFS=y
CONFIG_GRAPHICS_LVGL=y
CONFIG_HAVE_CXX=y
CONFIG_HAVE_CXXINITIALIZE=y
CONFIG_HEAP2_BASE=0xC07D0000
CONFIG_HEAP2_SIZE=25362432
CONFIG_INIT_ENTRYPOINT="nsh_main"
CONFIG_INPUT=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_INTELHEX_BINARY=y
CONFIG_LV_DEF_REFR_PERIOD=5
CONFIG_LV_FONT_MONTSERRAT_12=y
CONFIG_LV_FONT_MONTSERRAT_16=y
CONFIG_LV_FONT_MONTSERRAT_24=y
CONFIG_LV_NUTTX_VSYNC_TIMER_PERIOD=5
CONFIG_LV_USE_CLIB_MALLOC=y
CONFIG_LV_USE_CLIB_SPRINTF=y
CONFIG_LV_USE_CLIB_STRING=y
CONFIG_LV_USE_DEMO_WIDGETS=y
CONFIG_LV_USE_LOG=y
CONFIG_LV_USE_NUTTX=y
CONFIG_LV_USE_NUTTX_TOUCHSCREEN=y
CONFIG_LV_USE_PERF_MONITOR=y
CONFIG_LV_USE_SYSMON=y
CONFIG_MM_REGIONS=2
CONFIG_MQ_MAXMSGSIZE=64
CONFIG_NSH_ARCHINIT=y
CONFIG_NSH_BUILTIN_APPS=y
CONFIG_NSH_FILEIOSIZE=512
CONFIG_NSH_READLINE=y
CONFIG_PREALLOC_TIMERS=4
CONFIG_RAM_SIZE=114688
CONFIG_RAM_START=0x20000000
CONFIG_RAW_BINARY=y
CONFIG_RR_INTERVAL=200
CONFIG_SCHED_HPWORK=y
CONFIG_SCHED_WAITPID=y
CONFIG_START_DAY=15
CONFIG_START_MONTH=11
CONFIG_START_YEAR=2017
CONFIG_STM32_CCMEXCLUDE=y
CONFIG_STM32_DISABLE_IDLE_SLEEP_DURING_DEBUG=y
CONFIG_STM32_DMA2D=y
CONFIG_STM32_DMA2D_FB_BASE=0xC03E8000
CONFIG_STM32_DMA2D_FB_SIZE=4096000
CONFIG_STM32_DMA2D_LAYER_PPLINE=1024
CONFIG_STM32_EXTERNAL_RAM=y
CONFIG_STM32_FMC=y
CONFIG_STM32_JTAG_SW_ENABLE=y
CONFIG_STM32_LTDC=y
CONFIG_STM32_LTDC_FB_BASE=0xC0000000
CONFIG_STM32_LTDC_FB_SIZE=4096000
CONFIG_STM32_PWR=y
CONFIG_STM32_USART1=y
CONFIG_SYSTEM_NSH=y
CONFIG_TASK_NAME_SIZE=0
CONFIG_USART1_SERIAL_CONSOLE=y
CONFIG_VIDEO_FB=y
5. 硬件驱动实现
5.1 定义硬件宏
在src/apollo-stm32f429i.h和include/board.h中定义相关的硬件宏。
5.1.1 板级私有定义
apollo-stm32f429i.h文件包含板级特有的硬件定义:
c复制#ifndef __BOARDS_ARM_STM32_APOLLO_STM32F429I_SRC_APOLLO_STM32F429I_H
#define __BOARDS_ARM_STM32_APOLLO_STM32F429I_SRC_APOLLO_STM32F429I_H
#include <nuttx/config.h>
#include <nuttx/compiler.h>
#include <stdint.h>
#include <arch/stm32/chip.h>
#include "stm32_gpio.h"
/* LED definitions */
#define GPIO_LED_RED (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_PULLUP|GPIO_SPEED_50MHz|\
GPIO_OUTPUT_SET|GPIO_PORTB|GPIO_PIN1)
#define GPIO_LED_GREEN (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_PULLUP|GPIO_SPEED_50MHz|\
GPIO_OUTPUT_SET|GPIO_PORTB|GPIO_PIN0)
/* Button definitions */
#define MIN_IRQBUTTON BUTTON_KEY0
#define MAX_IRQBUTTON BUTTON_KEY3
#define NUM_IRQBUTTONS 4
#define GPIO_BTN_KEY0 (GPIO_INPUT|GPIO_PULLUP|GPIO_SPEED_50MHz|GPIO_EXTI|GPIO_PORTH|GPIO_PIN3)
#define GPIO_BTN_KEY1 (GPIO_INPUT|GPIO_PULLUP|GPIO_SPEED_50MHz|GPIO_EXTI|GPIO_PORTH|GPIO_PIN2)
#define GPIO_BTN_KEY2 (GPIO_INPUT|GPIO_PULLUP|GPIO_SPEED_50MHz|GPIO_EXTI|GPIO_PORTC|GPIO_PIN13)
#define GPIO_BTN_KEY3 (GPIO_INPUT|GPIO_PULLDOWN|GPIO_SPEED_50MHz|GPIO_EXTI|GPIO_PORTA|GPIO_PIN0)
/* Touchscreen definitions */
#define GT9XX_I2C_ADDRESS 0x14
#define GPIO_GT9XX_INT (GPIO_INPUT|GPIO_FLOAT|GPIO_EXTI|GPIO_SPEED_100MHz| \
GPIO_PORTH|GPIO_PIN7)
#define GPIO_GT9XX_RST (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_PULLUP|GPIO_SPEED_50MHz|\
GPIO_OUTPUT_SET|GPIO_PORTI|GPIO_PIN8)
/* LCD Backlight control */
#define GPIO_LCD_BL (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\
GPIO_OUTPUT_SET|GPIO_PORTB|GPIO_PIN5)
/* Public function prototypes */
int stm32_bringup(void);
#ifdef CONFIG_APOLLO_STM32F429I_CT_I2C
int stm32_tsc_setup(int minor);
#endif
#ifdef CONFIG_STM32_FMC
void stm32_sdram_initialize(void);
#endif
#endif /* __BOARDS_ARM_STM32_APOLLO_STM32F429I_SRC_APOLLO_STM32F429I_H */
5.1.2 公共板级定义
include/board.h文件包含时钟配置等公共定义:
c复制#ifndef __BOARDS_ARM_STM32_APOLLO_STM32F429I_INCLUDE_BOARD_H
#define __BOARDS_ARM_STM32_APOLLO_STM32F429I_INCLUDE_BOARD_H
#include <nuttx/config.h>
#ifndef __ASSEMBLY__
# include <stdint.h>
#endif
/* Clocking configuration */
#define STM32_BOARD_XTAL 25000000ul
#define STM32_HSI_FREQUENCY 16000000ul
#define STM32_LSI_FREQUENCY 32000
#define STM32_HSE_FREQUENCY STM32_BOARD_XTAL
#define STM32_LSE_FREQUENCY 32768
/* Main PLL Configuration */
#define STM32_PLLCFG_PLLM RCC_PLLCFG_PLLM(25)
#define STM32_PLLCFG_PLLN RCC_PLLCFG_PLLN(336)
#define STM32_PLLCFG_PLLP RCC_PLLCFG_PLLP_2
#define STM32_PLLCFG_PLLQ RCC_PLLCFG_PLLQ(7)
#define STM32_SYSCLK_FREQUENCY 168000000ul
/* AHB clock (HCLK) is SYSCLK (168MHz) */
#define STM32_RCC_CFGR_HPRE RCC_CFGR_HPRE_SYSCLK
#define STM32_HCLK_FREQUENCY STM32_SYSCLK_FREQUENCY
/* APB1 clock (PCLK1) is HCLK/4 (42MHz) */
#define STM32_RCC_CFGR_PPRE1 RCC_CFGR_PPRE1_HCLKd4
#define STM32_PCLK1_FREQUENCY (STM32_HCLK_FREQUENCY/4)
/* APB2 clock (PCLK2) is HCLK/2 (84MHz) */
#define STM32_RCC_CFGR_PPRE2 RCC_CFGR_PPRE2_HCLKd2
#define STM32_PCLK2_FREQUENCY (STM32_HCLK_FREQUENCY/2)
/* LED definitions */
#define BOARD_LED1 0
#define BOARD_LED2 1
#define BOARD_NLEDS 2
#define BOARD_LED1_BIT (1 << BOARD_LED1)
#define BOARD_LED2_BIT (1 << BOARD_LED2)
/* Button definitions */
#define BUTTON_KEY0 0
#define BUTTON_KEY1 1
#define BUTTON_KEY2 2
#define BUTTON_KEY3 3
#define NUM_BUTTONS 4
#define BUTTON_KEY0_BIT (1 << BUTTON_KEY0)
#define BUTTON_KEY1_BIT (1 << BUTTON_KEY1)
#define BUTTON_KEY2_BIT (1 << BUTTON_KEY2)
#define BUTTON_KEY3_BIT (1 << BUTTON_KEY3)
/* LCD configuration */
#ifdef CONFIG_STM32_LTDC
# ifdef CONFIG_APOLLO_STM32F429I_ATK7016_FBIFACE
# if defined(CONFIG_APOLLO_STM32F429I_ATK7016_FBIFACE_LANDSCAPE) || \
defined(CONFIG_APOLLO_STM32F429I_ATK7016_FBIFACE_RLANDSCAPE)
# define BOARD_LTDC_WIDTH 1024
# define BOARD_LTDC_HEIGHT 600
# else
# define BOARD_LTDC_WIDTH 600
# define BOARD_LTDC_HEIGHT 1024
# endif
# define BOARD_LTDC_OUTPUT_BPP 16
# define BOARD_LTDC_HFP 160
# define BOARD_LTDC_HBP 140
# define BOARD_LTDC_VFP 12
# define BOARD_LTDC_VBP 20
# define BOARD_LTDC_HSYNC 20
# define BOARD_LTDC_VSYNC 3
#endif
#endif
#endif /* __BOARDS_ARM_STM32_APOLLO_STM32F429I_INCLUDE_BOARD_H */
5.2 实现核心初始化函数
5.2.1 早期硬件初始化
stm32_boot.c文件实现早期硬件初始化:
c复制/****************************************************************************
* Name: stm32_boardinitialize
*
* Description:
* All STM32 architectures must provide the following entry point. This
* entry point is called early in the initialization -- after all memory
* has been configured and mapped but before any devices have been
* initialized.
*
****************************************************************************/
void stm32_boardinitialize(void)
{
#ifdef CONFIG_STM32_FMC
/* Initialize SDRAM */
stm32_sdram_initialize();
#endif
#ifdef CONFIG_ARCH_LEDS
/* Configure on-board LEDs if LED support has been selected. */
board_autoled_initialize();
#endif
#ifdef CONFIG_STM32_LTDC
/* Initialize the LCD */
stm32_lcd_initialize();
#endif
}
5.2.2 设备驱动初始化
stm32_bringup.c文件实现设备驱动初始化:
c复制/****************************************************************************
* Name: stm32_bringup
*
* Description:
* Perform architecture-specific initialization
*
****************************************************************************/
int stm32_bringup(void)
{
int ret;
#ifdef CONFIG_FS_PROCFS
/* Mount the proc filesystem */
ret = mount(NULL, "/proc", "procfs", 0, NULL);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: Failed to mount procfs at /proc: %d\n", ret);
}
#endif
#ifdef CONFIG_APOLLO_STM32F429I_CT_I2C
/* Initialize the touchscreen */
ret = stm32_tsc_setup(0);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: stm32_tsc_setup failed: %d\n", ret);
}
#endif
#ifdef CONFIG_INPUT_BUTTONS
/* Register the BUTTON driver */
ret = btn_lower_initialize("/dev/buttons");
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: btn_lower_initialize() failed: %d\n", ret);
}
#endif
#ifdef CONFIG_VIDEO_FB
/* Initialize and register the framebuffer driver */
ret = fb_register(0, 0);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: fb_register() failed: %d\n", ret);
}
#endif
return OK;
}
5.3 实现外部SDRAM驱动
stm32_extmem.c文件实现外部SDRAM驱动:
c复制/****************************************************************************
* Name: stm32_sdram_initialize
*
* Description:
* Called from stm32_bringup to initialize external SDRAM access.
*
****************************************************************************/
void stm32_sdram_initialize(void)
{
/* Enable FMC clock */
modifyreg32(STM32_RCC_AHB3ENR, 0, RCC_AHB3ENR_FMCEN);
/* Configure and enable the SDRAM bank1 */
stm32_sdram_init();
/* Enable the FMC SDRAM bank */
stm32_sdram_enable();
}
5.4 实现RGBLCD驱动
stm32_lcd.c文件实现RGBLCD驱动:
c复制/****************************************************************************
* Name: stm32_lcd_initialize
*
* Description:
* Initialize the LCD. This function should be called early in the boot
* sequence to initialize the LCD hardware.
*
****************************************************************************/
void stm32_lcd_initialize(void)
{
/* Enable LTDC clock */
modifyreg32(STM32_RCC_APB2ENR, 0, RCC_APB2ENR_LTDCEN);
/* Configure the LCD pins */
stm32_lcd_pinconfig();
/* Configure the LTDC */
stm32_ltdc_configure();
/* Enable the LTDC */
modifyreg32(STM32_LTDC_GCR, 0, LTDC_GCR_LTDCEN);
}
5.5 实现触摸屏驱动
stm32_touchscreen.c文件实现触摸屏驱动:
c复制/****************************************************************************
* Name: stm32_tsc_setup
*
* Description:
* This function is called by board-bringup logic to configure the
* touchscreen device.
*
****************************************************************************/
int stm32_tsc_setup(int minor)
{
struct gt9xx_config_s config;
FAR struct i2c_master_s *i2c;
int ret;
/* Initialize I2C */
i2c = stm32_i2cbus_initialize(CONFIG_GT9XX_I2C_BUS);
if (!i2c)
{
return -ENODEV;
}
/* Configure the GT911 touchscreen controller */
config.i2c = i2c;
config.addr = GT9XX_I2C_ADDRESS;
config.frequency = CONFIG_GT9XX_I2C_FREQUENCY;
config.irq = GPIO_GT9XX_INT;
config.rst = GPIO_GT9XX_RST;
/* Register the touchscreen device */
ret = gt9xx_register(&config, minor);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: gt9xx_register failed: %d\n", ret);
stm32_i2cbus_uninitialize(i2c);
}
return ret;
}
6. 编译与烧录
6.1 加载并配置项目
使用以下命令加载默认配置并进行菜单配置:
bash复制cd nuttx
./tools/configure.sh apollo-stm32f429i:lvgl
make menuconfig
在菜单配置界面中,可以根据需要调整各项配置参数。确保以下选项已正确配置:
- 图形子系统支持
- LVGL库支持
- 触摸屏驱动支持
- LCD驱动支持
6.2 编译代码
配置完成后,使用以下命令编译项目:
bash复制make -j$(nproc)
编译完成后,将在nuttx.bin和nuttx.hex文件中生成可执行文件。
6.3 烧录固件
使用ST-Link或J-Link调试器将编译生成的固件烧录到开发板中。以ST-Link为例:
bash复制st-flash write nuttx.bin 0x08000000
或者使用OpenOCD:
bash复制openocd -f interface/stlink.cfg -f target/stm32f4x.cfg -c "program nuttx.bin verify reset exit 0x08000000"
7. 运行与调试
7.1 连接串口
使用USB转串口工具连接开发板的USART1接口(PA9-TX, PA10-RX),在主机上使用串口终端工具(如Putty、Tera Term等)打开对应的串口,配置参数为:
- 波特率:115200
- 数据位:8
- 停止位:1
- 无校验
- 无流控
7.2 运行示例
开发板上电后,可以在串口终端中看到系统启动日志。如果一切正常,LVGL示例程序将自动运行,在LCD上显示图形界面,并且触摸屏功能可用。
8. 常见问题与解决方案
8.1 LCD显示异常
问题现象:LCD显示花屏、颜色异常或无法显示
可能原因:
- LTDC时钟配置不正确
- SDRAM初始化失败
- 帧缓冲区地址设置错误
解决方案: - 检查
board.h中的LTDC时钟配置 - 确认SDRAM初始化代码正确执行
- 验证帧缓冲区地址是否与链接脚本一致
8.2 触摸屏无响应
问题现象:LCD显示正常但触摸屏无响应
可能原因:
- I2C通信失败
- 触摸屏控制器初始化失败
- 中断引脚配置错误
解决方案: - 使用逻辑分析仪检查I2C信号
- 检查GT911初始化序列
- 验证中断引脚配置和中断服务程序
8.3 系统运行缓慢
问题现象:界面刷新缓慢,响应迟钝
可能原因:
- 系统时钟配置不正确
- 未启用DMA2D加速
- 内存访问速度慢
解决方案: - 检查系统时钟树配置
- 确保在配置中启用了
CONFIG_STM32_DMA2D - 优化内存访问模式
9. 性能优化建议
9.1 启用双缓冲
虽然默认配置中未启用双缓冲,但可以通过以下步骤手动启用:
- 在
Kconfig中确保CONFIG_STM32_LTDC_FB_DOUBLE_BUFFER=y - 修改
stm32_lcd.c中的帧缓冲区管理代码 - 调整链接脚本,确保有足够的空间分配两个帧缓冲区
9.2 使用DMA2D加速
LVGL支持使用DMA2D加速图形渲染,可以显著提高性能:
- 在配置中启用
CONFIG_STM32_DMA2D - 在LVGL配置中启用DMA2D支持
- 实现DMA2D相关的回调函数
9.3 优化内存使用
STM32F429的CCM RAM可以用于存储频繁访问的数据:
- 将LVGL的绘图缓冲区放在CCM RAM中
- 将关键数据结构标记为
__attribute__((section(".ccmram"))) - 在链接脚本中合理分配CCM RAM的使用
10. 扩展功能
10.1 添加更多LVGL示例
openvela支持多种LVGL示例程序,可以通过菜单配置选择:
bash复制make menuconfig
导航到:
code复制Application Configuration -> Examples -> LVGL Demo
选择需要的示例程序。
10.2 支持多语言
LVGL支持Unicode和多种语言显示:
- 在配置中启用
CONFIG_LV_USE_FREETYPE - 添加中文字体文件
- 实现多语言切换功能
10.3 添加网络支持
通过以太网或Wi-Fi模块实现远程控制:
- 添加LwIP协议栈支持
- 实现网络接口驱动
- 开发基于Web的配置界面
11. 开发心得
在实际开发过程中,以下几点经验值得分享:
-
时钟配置是关键:STM32的时钟树比较复杂,务必仔细验证每个时钟域的频率设置,特别是LTDC和SDRAM相关的时钟。
-
内存管理要谨慎:STM32F429虽然有256KB SRAM,但在高分辨率图形应用中仍然容易耗尽内存,需要精心规划内存使用。
-
中断优先级要合理:触摸屏中断、LTDC中断等需要设置合适的优先级,避免影响系统实时性。
-
调试工具很重要:逻辑分析仪对于调试I2C、SPI等接口问题非常有用,而STM32CubeMonitor可以帮助实时监控内存使用情况。
-
充分利用硬件加速:STM32F429的DMA2D、LTDC等外设可以显著减轻CPU负担,提高图形性能。
通过这个项目,我深刻体会到在资源受限的嵌入式系统上实现流畅图形界面的挑战和乐趣。openvela作为一个轻量级RTOS,与LVGL图形库的配合使用,为STM32开发者提供了一个强大的图形应用开发平台。