
简介STM32F103ZET6单片机实验例程源码包围绕STemWin图形库中的DropDwon下拉控件展开面向需要为STM32平台添加图形用户界面的嵌入式开发者和学习者。压缩包共包含536个文件涵盖头文件h、C源码c、汇编启动文件s/asm、库文件lib、可执行文件axf/hex等类型整体大小12.15MB可满足从源码阅读到直接烧录验证的完整流程。目前已有44人学习下载该例程演示了如何在STM32F103ZET6上集成STemWin并实现DropDwon控件提供可直接参考的工程模板与配置说明有助于开发者快速搭建下拉选择功能进而拓展出更复杂的GUI应用。通过分析和修改这些代码初学者可以理解STemWin的窗口管理与控件机制有经验的工程师也能将其作为基础模板加快项目落地并降低开发风险。资源还附带必要的支持文件和配置说明帮助读者将STemWin库集成进自己的MDK工程并提供了从基础绘图到控件交互的完整示例适合用于学习与二次开发。1. 这个源码包在讲什么STM32F103ZET6 上把 STemWin 下拉控件跑起来一块常见的 STM32F103ZET6 核心板屏是 FSMC 驱动的 3.5 寸 TFT工程里嵌了 ST 官方的 STemWin 库——这个标题描述的就是这类板子上最常见的 GUI 实验把 DropDwon 控件也就是下拉列表放到界面上并让它能弹开、能选中。STemWin 是 SEGGER emWin 在 ST MCU 上的授权版本数据手册之外很多人第一次接触它就是从这种控件例程开始的。别小看这个控件一个下拉框能显示几个状态、用户怎么从触摸屏上点出选项、选完后如何把值带回业务逻辑这套链路是仪表和配置型设备界面里出现频率最高的交互。例程源码的质量有高有低但它解决的问题很具体在 F103ZET6 的 64KB SRAM 里给 STemWin 足够的内存池然后把控件从创建到通知的调用走通。本文以这个标题为原点把 DropDwon 控件在 STM32F103ZET6 上的移植、创建、参数配置和故障定位完整展开。2. 动手前先理清STemWin 在 F103ZET6 上的内存与底层驱动配置STemWin 在 F103ZET6 上首先遇到的不是控件本身而是内存。GUI_ALLOC_SIZE 定义了控件使用的全局堆F103ZET6 的 64KB SRAM 里还要留给 RTOS、协议栈和业务代码一般给 8KB 到 16KB。给太小的话Dropdown 控件创建成功但下拉列表展开时要再次分配一个窗口缓冲区分配失败就表现为点击后没有任何反应。2.1 GUIConf.h / GUIConf.c 里该改哪几个参数实验例程里最常见的问题是打开文件后找不到 GUI_ALLOC_SIZE这是因为不同 STemWin 版本把配置放在不同位置但核心参数在 GUIConf.h 中固定那几项。宏建议初始值作用GUI_NUM_LAYERS1单显示层F103ZET6 不接双屏就写 1GUI_SUPPORT_TOUCH1支持触摸输入引出触摸驱动接口GUI_ALLOC_SIZE8192STemWin 内部堆大小影响可创建的控件数量GUI_DEFAULT_FONTGUI_Font6x8默认字体先用它验证屏幕// GUIConf.h 中常见的最小配置 #define GUI_NUM_LAYERS 1 #define GUI_SUPPORT_TOUCH 1 #define GUI_SUPPORT_MOUSE 0 #define GUI_ALLOC_SIZE 8192 #define GUI_DEFAULT_FONT GUI_Font6x8提示GUI_ALLOC_SIZE 的单位是字节不是控件个数。下拉控件在弹出列表时还要额外分配一个列表窗口内存所以初学者常犯的错误是堆给得太小界面能画但一点下拉就卡死或白屏。2.2 FSMC 并口屏优先SPI 屏留给验证F103ZET6 的 FSMC 接口可以模拟 8080 并口时序接 ILI9341/ILI9488 这类 3.5 寸屏非常顺畅。STemWin 官方驱动里对应的是 GUIDRV_FSMC 这类模板SPI 屏虽然省引脚但整屏刷新时 CPU 占用更高。这个例程里如果只是演示控件并不需要多快的刷新但后续接复杂菜单时并口屏在刷图片和拖动反馈上的优势会直接决定体验。常见做法是在 LCD_X_Config() 里指明驱动号再把像素坐标的访问函数填进屏幕驱动层// LCDConf.c 中的关键入口 void LCD_X_Config(void) { GUI_DEVICE_CreateAndLink(GUIDRV_FSMC_API, GUI_SHOW, 0); LCD_SetSizeEx(0, 480, 320); // 设置分辨率 LCD_SetVSizeEx(0, 480, 320); // 虚拟屏幕大小和实际一致 }这段代码的逻辑是GUIDRV_FSMC_API 告诉 STemWin 底层要用 FSMC 并口访问像素GUI_SHOW 表示该显示层立即可见后面两个 SetSizeEx 决定上层 GUI 看到的画布大小。若你的屏是 320x240就把 480、320 改成 320、240否则控件坐标会超出边界DropDwon 弹出的列表可能画到屏幕外。2.3 无控件最小显示测试先确认 GUI 已经能写字不急着碰下拉控件先写一屏文字用来排除“STemWin 没跑起来”和“LCD 驱动没初始化”这两类问题。void STemWin_Init(void) { GUI_Init(); GUI_SetBkColor(GUI_BLACK); GUI_Clear(); GUI_SetColor(GUI_WHITE); GUI_DispStringAt(STemWin on STM32F103ZET6, 20, 20); }GUI_Init() 会调用 LCD_X_Config() 完成底层屏幕注册并初始化之后 SetBkColor/Clear 负责清屏DispStringAt 在指定坐标写字。看到白字就说明底层通了之后所有控件例程都可以直接在这个工程上叠加白屏则先查 FSMC 引脚初始化、片选信号是否给对以及屏的复位时序是否满足 ILI9341 手册要求。别在屏幕都没点亮时就去调试 DROPDOWN那会把驱动问题和控件问题混在一起浪费一整天。3. 创建第一个 DropDwon 控件API 调用与回调事件处理STemWin 里下拉控件的类名是 DROPDOWN在 emWin 手册中归属 WIDGET 类。控件本身由一个按钮样式的当前选择框和一个隐藏的 LISTBOX 弹出列表组成理解这个结构对后面调试很有帮助你看到的是“一个东西”实际上占用的窗口是两个内存消耗也要按两个控件估算。3.1 DROPDOWN_CreateEx 与 DROPDOWN_AddString 的参数例程中创建下拉框通常用下面这段代码我建议直接把创建和填充分开写方便后期把选项数据改成从结构体数组循环加载static DROPDOWN_Handle _hDd; void CreateDropdown(void) { // 在父窗口坐标(20, 40)创建宽200高24 _hDd DROPDOWN_CreateEx(20, 40, 200, 24, _hParent, WM_CF_SHOW, GUI_TA_LEFT, 0, 0); // 循环添加选项这里的选项表可以来自业务配置 const char *options[] {通道1, 通道2, 通道3, 通道4, 自动}; for (int i 0; i 5; i) { DROPDOWN_AddString(_hDd, options[i]); } // 默认选中第0项 DROPDOWN_SetSel(_hDd, 0); }CreateEx 的后三个参数容易混淆GUI_TA_LEFT 管文本对齐0 是附加 Ex 参数最后一个 0 用于调整滚动条和附加控件标志一般固定传 0。DROPDOWN_AddString 每调用一次就复制一次字符串所以选项内容必须保证是静态存储期比如字符串常量或 static 数组不能传栈上临时缓冲的地址否则控件显示乱码。3.2 让点击下拉项的结果回传DROPDOWN_NOTIFICATION_SEL_CHANGED仅仅能添加选项还不够“实验例程”和“能用的菜单”之间就差一个通知。STemWin 的回调事件里当用户点击下拉框并选中新项时父窗口收到 WM_NOTIFY_PARENT并在额外数据里带上 DROPDOWN_NOTIFICATION_SEL_CHANGED。处理逻辑一般写在父窗口回调的 WM_NOTIFY_PARENT 分支里case WM_NOTIFY_PARENT: { WM_HWIN hItem WM_GetDialogItem(pMsg-hWin, ID_DROPDOWN_1); if (pMsg-Data.v DROPDOWN_NOTIFICATION_SEL_CHANGED) { int sel DROPDOWN_GetSel(_hDd); char buf[16]; sprintf(buf, sel%d, sel); GUI_DispStringAt(buf, 20, 80); } break; }pMsg-Data.v 存放的是通知码比较它即可判断是否是下拉选择变化事件。DROPDOWN_GetSel 返回当前选中的索引从 0 开始。注意这里不能在通知里直接调用 DROPDOWN_Delete因为事件是在控件消息处理流程内派发的立即删掉控件可能造成野指针访问。3.3 程序里主动展开与收起别等用户去点鼠标点击会自己弹开但很多例程为了演示“按键控制”会主动调用展开和收起函数DROPDOWN_Expand(_hDd); // 展开下拉列表 DROPDOWN_Collapse(_hDd); // 收起下拉列表这两个函数内部发送的是控件控制消息实际效果等价于模拟一次鼠标点击。用按键驱动时建议在按键松开的处理函数里调用避免长按导致高频重复展开收起如果在 FreeRTOS 下跑还要注意 DROPDOWN_Expand 之后要让出时间片等待列表重绘否则界面会只出现一个半透明的边框要再调一次 GUI_ExecIdle() 强制刷新列表内容才完整显示。4. 调整显示参数字体、颜色、列表高度与滚动行为下拉控件好不好用一半取决于 API 逻辑另一半取决于显示参数。很多例程默认字体是 6x8在一个 480x320 的屏上看起来很小但这不单是字体美观问题——下拉列表的每一项高度由字体高度决定字体设小了用户在 3.5 寸电阻屏上很难用手指点中。4.1 字体和行高一起改否则触摸目标太小DROPDOWN_SetFont(_hDd, GUI_Font16_1); // 下拉框内字体 DROPDOWN_SetItemHeight(_hDd, 20); // 每项高度20像素 DROPDOWN_SetTextColor(_hDd, GUI_TA_LEFT | GUI_WHITE, GUI_BLACK);最后的 SetTextColor 第三个参数是选中项的背景色第二个是文本颜色。常见误用是把顺序写反导致文字黑底黑字完全看不清。GUI_Font16_1 是中文字库还是 ASCII 字库由 STemWin 的字库文件决定例程里若选项包含中文务必确认工程链接过中文字库否则显示为空心方块。4.2 滚动条、展开方向与弹出高度用三个函数控制弹出列表下拉弹出的列表在空间不足时不会自动收缩而是撑开一个比控件大的窗口。所以要在创建后限定弹出列表的最大高度DROPDOWN_SetScrollbarHeight(_hDd, 10); // 滚动条宽度 DROPDOWN_SetAutoScroll(_hDd, 1); // 自动滚动到选中项可见 DROPDOWN_SetPopupHeight(_hDd, 120); // 弹出列表最大高度参数含义不设置时的表现ScrollbarHeight滚动条宽/高项数多时列表右侧无滚动条AutoScroll是否自动滚到选中项弹出时总是从第一项开始PopupHeight弹出列表最大像素高度列表高度等于内容高度可能超出屏幕如果弹出列表底边超出屏幕用 DROPDOWN_SetPopupHeight 主动限制例程里没写这条但实际产品菜单上一定会碰到。想要列表向上弹出而不是向下展开可以创建控件时在 Ex 参数里带 DROPDOWN_CF_UP 标志不过这个标志在不同版本 STemWin 里的定义可能不同使用前要在头文件里确认。4.3 自定义绘制把默认白底的下拉框改成产品风格STemWin 的默认皮肤在 F103ZET6 上偏“工程师风”想贴近产品可以直接把下拉控件的皮肤函数换成内置的经典样式DROPDOWN_SetDefaultSkinClassic(); DROPDOWN_SetSkin(_hDd, DROPDOWN_SKIN);DROPDOWN_SKIN 是库内部导出的宏不同的 STemWin 版本它的函数签名不同用之前去 emWin 头文件里查一下 DROPDOWN_SKIN 的实际定义如果例程包里自带 Custom 皮肤通常会多一个 DROPDOWN_CUSTOM_SetDrawBkg 之类的函数入口按照它的参数签名仿写一个画背景的回调即可。要注意设置了自定义皮肤后之前调用的 SetTextColor / SetItemHeight 不一定还生效因为皮肤回调会接管整个绘制流程颜色和行高要在皮肤回调里自己画。5. 用最小用例验证 DropDwon白屏、弹不开、选不中的定位三步这一节不写总结给一套能复用的验证流程和三个高频踩坑点的定位方法。5.1 从串口打印到 SWD 单步验证顺序怎么走把上面的代码集成到工程后先编译烧录用串口或 SWD 仿真器单步确认 GUI_Init() 返回无误然后在 GUI_DispStringAt 那行打一个断点确认文字显示成功。接着创建下拉控件用鼠标或触摸笔点击下拉框若列表弹开且选项背景随触摸移动说明 DROPDOWN 通知链路已通。之后修改 DROPDOWN_SetSel(1)观察默认选中项是否切换这样就把 Create、Expand、Selection、Notify 四个环节全走了一遍。5.2 三个排障案例堆分配失败、乱码、坐标偏移坑一下拉控件创建后整个界面不刷新——先查 GUI_ALLOC_SIZE。F103ZET6 上推荐从 8KB 往上加加到 12KB 依然白屏就要查是否在调用 DROPDOWN_CreateEx 前执行了 GUI_ExecDialogBox 或 GUI_ExecCreatedDialog 这类模态循环模态循环会把创建控件的窗口消息阻塞住。坑二选项显示乱码——选项数据必须指向静态字符串。可以在 DROPDOWN_AddString 后保留字符串在 Flash 中最稳妥的做法是建一个 const char* 指针数组再用循环读取而不是用 sprintf 临时拼接。sprintf 指向临时栈变量控件只保存指针不拷贝内容函数返回到栈一释放乱码自然出现。坑三触摸选不中下拉项——多数是触摸坐标与 LCD 坐标不一致。在 LCD_X_Config 里 LCD_SetVSizeEx 设置的分辨率如果和实际屏参数不符会导致 GUI 坐标系被拉伸点击区域偏移。用以下代码做坐标回显验证// 在触摸事件里打印触摸位置观察坐标是否和屏幕位置一致 GUI_PID_STATE State; GUI_PID_GetState(State); printf(tch x%d y%d pressed%d\n, State.x, State.y, State.Pressed);对照按下位置和打印值如果 y 值普遍偏小且比例固定优先检查 LCD_SetVSizeEx 两个参数是否与屏规格书中的输出分辨率一致。另外在 Proteus 里仿真 STM32F103ZET6 时虚拟 LCD 的时序和触摸坐标跟真机有明显差异DROPDOWN 的弹出列表在仿真里看起来正常并不代表真机点击区域就对最终验证务必回到开发板上跑一遍触摸回显。经过以上验证后下拉控件的应用闭环就完整了创建、填充、选择、通知、样式、故障定位都有了可执行的路径。后续你想把它接进实际业务菜单只需把 DROPDOWN_GetSel 的返回值映射到不同的配置项替换掉例子里的调试代码即可。本文还有配套的精品资源点击获取