简介: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_LAYERS | 1 | 单显示层,F103ZET6 不接双屏就写 1 |
| GUI_SUPPORT_TOUCH | 1 | 支持触摸输入,引出触摸驱动接口 |
| GUI_ALLOC_SIZE | 8192 | STemWin 内部堆大小,影响可创建的控件数量 |
| GUI_DEFAULT_FONT | &GUI_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 的返回值映射到不同的配置项,替换掉例子里的调试代码即可。
本文还有配套的精品资源,点击获取