1. SWIPELIST控件嵌入式GUI中的滑动列表利器在嵌入式GUI开发里列表控件几乎是每个交互界面的标配。从简单的设置菜单到复杂的文件浏览器列表承载了信息展示和用户选择的核心功能。emWin作为老牌且功能强大的嵌入式图形库其内置的SWIPELIST控件在我看来是解决这类需求的一个非常“趁手”的工具。它不仅仅是一个静态的列表更集成了滑动操作、项目分组、图文混排等现代交互特性。初次接触官方手册时你可能会被那几十个API函数和一堆配置宏吓到觉得这东西很复杂。但实际用下来你会发现它的设计逻辑非常清晰一旦掌握了几个核心函数和设计模式就能快速构建出既美观又流畅的列表界面。这篇文章我就结合自己多次在STM32、NXP等MCU平台上的使用经验来拆解一下SWIPELIST控件的配置、API使用以及一些实用的界面设计技巧希望能帮你绕过我当初踩过的那些坑。2. 核心概念与设计思路拆解2.1 SWIPELIST是什么解决什么问题简单来说SWIPELIST是一个支持垂直滑动的列表控件。它的核心价值在于在资源有限的嵌入式设备上提供了一种高效管理长列表内容并实现流畅滑动交互的解决方案。你想想一个设备的设置项可能有几十个全部显示在一屏里不现实传统的“上一页/下一页”按钮又很笨拙。SWIPELIST通过触摸或编码器滑动来浏览内容交互更自然体验更接近智能手机。它和emWin里另一个列表控件LISTBOX有什么区别这是很多人会问的。LISTBOX更偏向于传统的单选/多选列表交互以点击选择为主虽然也能滚动但滑动体验和视觉效果的定制性不如SWIPELIST。SWIPELIST的每个列表项Item可以看作一个独立的“画布”你可以在上面自由组合文本、位图甚至嵌入其他窗口控件比如一个开关按钮灵活性高得多。所以当你需要构建一个带有复杂项内容如图标多行文本附加控件且强调滑动浏览体验的列表时SWIPELIST是更合适的选择。2.2 核心组件Item与Separator Item理解SWIPELIST首先要吃透它的两个基本构成单元普通项Item和分隔项Separator Item。这是它实现内容结构化展示的关键。普通项Item这是列表的主体代表一个可交互的选项。每个Item可以包含标题文本Header Text通过SWIPELIST_AddItem()添加的主文本通常用于项的名称。附加文本Additional Text通过SWIPELIST_AddItemText()添加的更多文本行用于显示详情、状态等。位图Bitmap通过SWIPELIST_SetBitmap()设置的图标支持多种对齐方式。用户数据User Data一个32位的自定义数据U32通过SWIPELIST_SetItemUserData()关联这是实现业务逻辑的关键。比如你可以在这里存储一个代表具体功能的枚举值。附着窗口Attached Window你甚至可以将一个按钮BUTTON、滑块SLIDER等其他控件的窗口句柄“贴”到这个Item上实现复合交互。这个功能非常强大后面会详细讲。分隔项Separator Item顾名思义用于将列表按主题分组。它通过SWIPELIST_AddSepItem()创建。分隔项有以下几个关键特性不可选择用户点击或滑动到它时不会触发WM_NOTIFICATION_SEL_CHANGED等选择相关的通知。视觉区分默认使用不同的字体和颜色如更大的字体、不同的背景色使其在视觉上成为一组项目的“标题”。无分隔线在分隔项之前上一个普通项底部的分隔线会被自动隐藏尺寸设为0这使得分组看起来更整体。官方手册里那个对比表格很清晰我把它用更直白的话总结一下普通项是“可操作的选项”而分隔项是“不可操作的分类标题”。合理使用分隔项能让一个长长的列表变得井井有条极大提升用户体验。2.3 视觉与交互配置解析SWIPELIST提供了极其细致的视觉和交互参数配置这既是其强大之处也是初上手时容易困惑的地方。这些配置主要分为两类默认配置和实例配置。默认配置Default Configuration以SWIPELIST_SetDefaultBkColor、SWIPELIST_SetDefaultFont等函数为代表。这些函数设置的是“全局默认值”影响的是此后新创建的SWIPELIST控件。比如你在main()函数初始化阶段调用SWIPELIST_SetDefaultFont(0, GUI_Font16_1)那么之后程序中创建的所有SWIPELIST控件的标题字体默认都是16点阵字体。这适合用来定义整个应用的视觉主题。实例配置Instance Configuration以SWIPELIST_SetBkColor、SWIPELIST_SetFont等函数为代表。这些函数操作的是一个特定的、已存在的SWIPELIST控件句柄hObj。你可以用它来覆盖某个特定列表的默认样式实现个性化。这里有个非常重要的细节实例配置的优先级高于默认配置且通常在创建控件后、添加项目前进行设置这样能确保后续添加的项目都应用新样式。几个关键的配置维度颜色系统背景色、文本色、分隔线颜色都有“选中SEL”和“未选中UNSEL”两种状态。这让你可以轻松实现高亮效果。颜色索引如SWIPELIST_CI_ITEM_BK_SEL是这些API的Index参数必须查手册准确使用。尺寸与边距SWIPELIST_SetBorderSize控制项内容与项边框的内部间距上、下、左、右。SWIPELIST_SetBitmapSpace控制项内文字与位图之间的间隙。这些像素级的控制是实现精准UI布局的基础。滑动参数SWIPELIST_SetThreshold设置触发滑动操作的最小像素位移默认5防止误触。SWIPELIST_SetOverlap设置滑动时的“重叠”距离影响滑动的阻尼感和回弹效果设为0则无重叠。实操心得一配置的顺序很重要我建议的配置顺序是1) 设置全局默认值可选定义主题2) 创建SWIPELIST控件SWIPELIST_CreateEx3) 对该控件实例进行个性化配置颜色、字体、边距等4) 添加Item和Separator Item5) 必要时对单个Item进行微调如单独设置某个项的分隔线颜色。这个顺序能避免样式混乱和重复绘制。3. 核心API详解与实战编程看完了概念和配置我们进入实战环节。手册里API函数很多但掌握核心的十几个就足以应对90%的需求。我会按照一个典型的创建、配置、使用流程来讲解。3.1 控件的创建与基础配置一切始于创建。SWIPELIST_CreateEx是最常用的创建函数。// 示例创建一个SWIPELIST控件 WM_HWIN hParent ...; // 父窗口句柄比如一个对话框 int x0 10, y0 50; // 在父窗口中的坐标 int xSize 220, ySize 300; // 控件宽度和高度 int Id GUI_ID_SWIPELIST0; // 控件ID用于消息回调识别 SWIPELIST_Handle hSwipeList; hSwipeList SWIPELIST_CreateEx(x0, y0, xSize, ySize, hParent, WM_CF_SHOW, 0, Id); if (hSwipeList 0) { // 创建失败处理 }创建成功后我们获得一个句柄hSwipeList。接下来进行基础配置比如设置项的背景色和字体// 设置未选中项的背景色为浅灰色选中项背景色为蓝色 SWIPELIST_SetBkColor(hSwipeList, SWIPELIST_CI_ITEM_BK_UNSEL, GUI_GRAY); SWIPELIST_SetBkColor(hSwipeList, SWIPELIST_CI_ITEM_BK_SEL, GUI_BLUE); // 设置项的标题字体和内容字体 SWIPELIST_SetFont(hSwipeList, SWIPELIST_FI_ITEM_HEADER, GUI_Font16_1); SWIPELIST_SetFont(hSwipeList, SWIPELIST_FI_ITEM_TEXT, GUI_Font13_1); // 设置项内部的边距让内容不紧贴边框 SWIPELIST_SetBorderSize(hSwipeList, SWIPELIST_BI_LEFT, 10); SWIPELIST_SetBorderSize(hSwipeList, SWIPELIST_BI_RIGHT, 10); SWIPELIST_SetBorderSize(hSwipeList, SWIPELIST_BI_TOP, 5); SWIPELIST_SetBorderSize(hSwipeList, SWIPELIST_BI_BOTTOM, 5);3.2 添加内容Item与Separator Item控件架子搭好了现在往里填充内容。添加一个普通项// 添加一个高度为50像素的项标题为“Wi-Fi设置” int itemIndex1 SWIPELIST_AddItem(hSwipeList, Wi-Fi设置, 50); if (itemIndex1 0) { // 添加成功可以为这个项添加详情文本 SWIPELIST_AddItemText(hSwipeList, itemIndex1, 已连接MyHome); // 关联用户数据例如用一个枚举值表示功能ID SWIPELIST_SetItemUserData(hSwipeList, itemIndex1, (U32)MENU_ID_WIFI); }SWIPELIST_AddItem的第三个参数ItemSize是项的高度像素。如果高度不足以显示文本控件会自动调整。sText参数为NULL则该项没有标题文本。添加一个分隔项来分组// 添加一个高度为30像素的分隔项作为分组标题 int sepIndex SWIPELIST_AddSepItem(hSwipeList, 网络设置, 30);分隔项创建后它前面的普通项本例中是“Wi-Fi设置”项底部的分隔线会自动消失视觉上“Wi-Fi设置”就归到了“网络设置”这个组下面。3.3 高级功能位图与附着窗口添加位图图标 想让列表项更美观图标少不了。首先你需要将图片转换成emWin可用的位图数组通常使用Segger的BMPCvt工具。假设你有一个bmWifiIcon的位图资源。// 为刚才创建的“Wi-Fi设置”项添加一个位图靠左垂直居中显示 SWIPELIST_SetBitmap(hSwipeList, itemIndex1, GUI_TA_LEFT | GUI_TA_VCENTER, bmWifiIcon); // 调整位图与文本之间的间距 SWIPELIST_SetBitmapSpace(hSwipeList, 8);位图对齐标志GUI_TA_LEFT,GUI_TA_RIGHT,GUI_TA_HCENTER,GUI_TA_TOP,GUI_TA_BOTTOM,GUI_TA_VCENTER可以组合使用。特别注意如果同时设置了水平和垂直居中GUI_TA_HCENTER | GUI_TA_VCENTER该项将只显示位图不显示任何文本。附着窗口——实现交互式列表项 这是SWIPELIST最强大的功能之一。你可以在一个列表项里嵌入一个开关、一个滑块或者一个进度条。// 假设我们要在“亮度调节”项后面加一个滑块 int itemIndex2 SWIPELIST_AddItem(hSwipeList, 亮度调节, 60); // 创建一个滑块控件注意其父窗口是SWIPELIST控件本身 SLIDER_Handle hSlider SLIDER_CreateEx(150, 10, 60, 20, hSwipeList, WM_CF_SHOW, 0, GUI_ID_SLIDER0); SLIDER_SetRange(hSlider, 0, 100); SLIDER_SetValue(hSlider, 70); // 将滑块附着到“亮度调节”项上位置(150, 10)是相对于该项左上角的坐标 SWIPELIST_ItemAttachWindow(hSwipeList, itemIndex2, hSlider, 150, 10);现在这个滑块就成了列表项的一部分会随着列表一起滑动。关键点附着窗口的父窗口hParent必须设为SWIPELIST的句柄hSwipeList而不是SWIPELIST的父窗口。它的坐标是相对于所附着项的内部区域的。3.4 处理用户交互通知与回调用户滑动、点击列表SWIPELIST会通过emWin的消息机制通知其父窗口。我们需要在父窗口的回调函数中处理这些消息。static void _cbDialog(WM_MESSAGE * pMsg) { switch (pMsg-MsgId) { case WM_NOTIFY_PARENT: { int Id WM_GetId(pMsg-hWinSrc); // 获取触发消息的控件ID int NCode pMsg-Data.v; // 通知代码 if (Id GUI_ID_SWIPELIST0) { switch (NCode) { case WM_NOTIFICATION_CLICKED: // 项被点击按下 break; case WM_NOTIFICATION_RELEASED: { // 项被释放点击完成这是最常用的 int releasedItem SWIPELIST_GetReleasedItem(pMsg-hWinSrc); if (releasedItem 0) { // 获取该项的用户数据判断具体功能 U32 userData SWIPELIST_GetItemUserData(pMsg-hWinSrc, releasedItem); _ExecuteCommand((MenuID_t)userData); } break; } case WM_NOTIFICATION_SEL_CHANGED: // 当前选中的项改变了滑动或点击不同项 int selItem SWIPELIST_GetSelItem(pMsg-hWinSrc); // 可以在这里更新其他UI比如显示选中项的详情 break; } } break; } // ... 处理其他消息 } }WM_NOTIFICATION_RELEASED是最可靠的选择确认消息。通过SWIPELIST_GetReleasedItem获取被释放项索引再通过SWIPELIST_GetItemUserData取出预先存储的命令ID这是连接UI与业务逻辑的标准做法。4. 界面设计实践与性能优化4.1 构建一个完整的设置菜单让我们综合运用以上知识快速构建一个设备设置菜单。void CreateSettingsMenu(SWIPELIST_Handle hList) { // 1. 配置列表整体样式 SWIPELIST_SetBkColor(hList, SWIPELIST_CI_ITEM_BK_UNSEL, GUI_DARKGRAY); SWIPELIST_SetBkColor(hList, SWIPELIST_CI_ITEM_BK_SEL, GUI_BLUE); SWIPELIST_SetFont(hList, SWIPELIST_FI_ITEM_HEADER, GUI_Font16B_1); // 标题用粗体 SWIPELIST_SetFont(hList, SWIPELIST_FI_ITEM_TEXT, GUI_Font13_1); SWIPELIST_SetTextColor(hList, SWIPELIST_CI_ITEM_TEXT_UNSEL, GUI_WHITE); SWIPELIST_SetTextColor(hList, SWIPELIST_CI_ITEM_TEXT_SEL, GUI_WHITE); // 2. 添加“显示设置”分组 SWIPELIST_AddSepItem(hList, 显示设置, 35); int itemBright SWIPELIST_AddItem(hList, 亮度, 55); SWIPELIST_AddItemText(hList, itemBright, 调节屏幕亮度); SWIPELIST_SetItemUserData(hList, itemBright, MENU_ID_BRIGHTNESS); SWIPELIST_SetBitmap(hList, itemBright, GUI_TA_LEFT | GUI_TA_VCENTER, bmBrightness); int itemTimeout SWIPELIST_AddItem(hList, 息屏时间, 50); SWIPELIST_AddItemText(hList, itemTimeout, 30秒); SWIPELIST_SetItemUserData(hList, itemTimeout, MENU_ID_SCREEN_TIMEOUT); // 可以在这里附着一个小窗口显示当前值或者用AddItemText动态更新 // 3. 添加“声音设置”分组 SWIPELIST_AddSepItem(hList, 声音设置, 35); int itemVolume SWIPELIST_AddItem(hList, 音量, 60); SWIPELIST_SetItemUserData(hList, itemVolume, MENU_ID_VOLUME); // 附着滑块控件 SLIDER_Handle hVolSlider SLIDER_CreateEx(160, 15, 50, 20, hList, WM_CF_SHOW, 0, 0); SLIDER_SetRange(hVolSlider, 0, 100); SWIPELIST_ItemAttachWindow(hList, itemVolume, hVolSlider, 160, 15); int itemRingtone SWIPELIST_AddItem(hList, 铃声, 50); SWIPELIST_AddItemText(hList, itemRingtone, 默认铃声); SWIPELIST_SetItemUserData(hList, itemRingtone, MENU_ID_RINGTONE); // 4. 添加“系统信息”项无分组 int itemInfo SWIPELIST_AddItem(hList, 关于本机, 50); SWIPELIST_SetItemUserData(hList, itemInfo, MENU_ID_ABOUT); SWIPELIST_SetBitmap(hList, itemInfo, GUI_TA_LEFT | GUI_TA_VCENTER, bmInfo); }这个例子展示了分组、图文混排、附着交互控件等多种技术的结合。Separator Item清晰地区分了不同设置模块。4.2 动态更新与内存管理嵌入式界面经常需要动态更新内容。例如在“Wi-Fi设置”项里需要根据连接状态更新附加文本。void UpdateWifiStatus(SWIPELIST_Handle hList, int itemIndex, const char* status) { // 先获取原有文本数量通常标题是第一个文本(索引0) // 这里我们假设状态是第二个文本(索引1) SWIPELIST_SetText(hList, itemIndex, 1, status); // 动态更新文本 // 如果需要也可以根据状态改变位图 if (strcmp(status, 已连接) 0) { SWIPELIST_SetBitmap(hList, itemIndex, GUI_TA_LEFT | GUI_TA_VCENTER, bmWifiConnected); } else { SWIPELIST_SetBitmap(hList, itemIndex, GUI_TA_LEFT | GUI_TA_VCENTER, bmWifiDisconnected); } }内存管理注意事项SWIPELIST内部会管理项目、文本字符串的内存。当你调用SWIPELIST_DeleteItem删除一个项时其关联的所有文本、用户数据以及附着窗口都会被清理。但是如果你附着了一个自己创建的窗口如上面的滑块当删除项时这个窗口也会被自动删除。如果你希望在其他地方继续使用该窗口务必先调用SWIPELIST_ItemDetachWindow将其分离。4.3 性能优化要点在资源紧张的MCU上GUI性能至关重要。使用SWIPELIST时以下几点能有效提升体验避免频繁重绘不要在循环里不停地调用SWIPELIST_SetText或SWIPELIST_SetBitmap来更新内容。应该仅在状态确实改变时更新或者使用WM_InvalidateWindow触发局部重绘。位图优化使用颜色深度合适的位图如对于黑白屏使用1bpp位图。emWin支持多种位图格式选择资源占用最小的。项目数量虽然SWIPELIST能处理很多项但一次加载过多比如超过50个会占用大量RAM并影响滑动响应。对于超长列表考虑实现“虚拟列表”只动态创建和渲染可视区域内的项。emWin本身不直接支持虚拟列表需要自己管理项的生命周期。禁用非必要特效如果性能吃紧可以考虑将选中项的背景色变化SWIPELIST_CI_ITEM_BK_SEL设置为与未选中项相同减少重绘区域。也可以适当增大SWIPELIST_SetThreshold的值降低滑动检测的灵敏度以节省CPU周期。5. 常见问题与调试技巧实录即使理解了原理和API实际开发中还是会遇到各种奇怪的问题。下面是我总结的一些典型坑点和解决方法。5.1 问题排查速查表现象可能原因排查步骤与解决方案列表显示空白1. 控件未创建成功。2. 创建后未添加任何Item。3. 控件尺寸或位置错误可能在屏幕外。4. 父窗口未有效显示或刷新。1. 检查SWIPELIST_CreateEx返回值是否为0。2. 确保在创建后调用了SWIPELIST_AddItem或SWIPELIST_AddSepItem。3. 使用WM_ShowWindow(hSwipeList)显式显示并用WM_InvalidateWindow(hParent)刷新父窗口。点击/滑动无反应1. 父窗口回调函数未处理WM_NOTIFY_PARENT消息。2. 控件被其他窗口如背景图遮挡。3. 触摸或输入设备驱动未正确关联到该窗口。1. 在父窗口回调的WM_NOTIFY_PARENT分支中检查控件ID和通知码。2. 使用emWin的调试工具如GUIBuilder检查窗口层级。3. 确保WM_SetCapture或触摸回调正确传递了输入事件。文本或位图显示不全1. 项的高度ItemSize设置太小。2. 位图尺寸过大超出了项的内部区域。3. 边框BorderSize设置过大挤压了内容空间。1. 增加SWIPELIST_AddItem的ItemSize参数值。2. 使用SWIPELIST_GetItemSize获取当前项高并检查位图尺寸。3. 减小SWIPELIST_SetBorderSize的值或调整位图对齐方式。Separator Item未正确分组Separator Item添加的顺序有误。Separator Item只对其之后添加的普通项起到分组作用直到下一个Separator Item出现。确保分组标题Separator Item添加在组内第一个普通项之前。附着窗口不显示或位置错乱1. 附着窗口的父窗口句柄设置错误。2. 附着坐标超出了项的范围。3. 附着窗口在项被创建之前就创建了。1.必须将附着窗口的父窗口设为SWIPELIST句柄hSwipeList。2. 坐标是相对于项内部的(0,0)点。计算好位置确保在项的高度和宽度内。3. 确保先调用SWIPELIST_AddItem再创建附着窗口并调用SWIPELIST_ItemAttachWindow。滑动卡顿或不跟手1. 系统负载过高GUI任务优先级低或执行时间过长。2. 内存不足导致动态分配失败。3. 位图过大或使用了抗锯齿字体绘制耗时。1. 提高GUI任务的优先级确保其能及时响应定时器或触摸中断。2. 监控堆栈使用优化项目数量避免在滑动过程中进行复杂计算或IO操作。3. 换用更小的位图或点阵字体或者使用存储设备Memory Device进行局部缓存绘制。5.2 调试心得与高级技巧利用WM_DEBUG功能在emWin的配置文件中启用WM_DEBUG可以在调试串口输出窗口管理器的消息流帮助你确认WM_NOTIFICATION_CLICKED等消息是否被正确发送和接收。自定义绘制Owner Draw应对复杂需求虽然SWIPELIST功能已经很强但如果你需要完全自定义某个项的外观比如绘制一个圆角背景、渐变颜色可以使用SWIPELIST_SetOwnerDraw函数。你需要提供一个自定义的绘制回调函数在这个函数里你可以调用SWIPELIST_OwnerDraw()来执行默认绘制然后在其基础上进行自己的绘制操作或者完全重绘。注意这会显著增加CPU负载谨慎使用。处理长文本与自动换行SWIPELIST本身不提供自动换行功能。如果你的文本可能很长需要在添加前手动计算。可以使用GUI_GetStringDistX()函数结合字体信息估算文本宽度如果超过项宽度就手动截断并添加省略号“...”或者拆分成多行通过多次调用SWIPELIST_AddItemText添加。实现“返回顶部”或“加载更多”通过监听WM_NOTIFICATION_SEL_CHANGED消息并调用SWIPELIST_GetScrollPos()获取当前滚动位置。当滚动到顶部位置为0或底部位置接近内容总高度减去控件高度时可以触发相应的操作例如刷新列表或加载历史数据。这需要你自己计算内容的总高度可以通过累加所有项的ItemSize和分隔线尺寸来获得。最后再分享一个我常用的初始化模板它建立了一个具有良好默认样式的SWIPELIST可以作为项目的基础SWIPELIST_Handle CreateStyledSwipeList(int x, int y, int width, int height, WM_HWIN hParent, int Id) { SWIPELIST_Handle hList SWIPELIST_CreateEx(x, y, width, height, hParent, WM_CF_SHOW, 0, Id); if (hList) { // 设置清爽的现代风格 SWIPELIST_SetBkColor(hList, SWIPELIST_CI_ITEM_BK_UNSEL, GUI_WHITE); SWIPELIST_SetBkColor(hList, SWIPELIST_CI_ITEM_BK_SEL, 0x00C0E0FF); // 浅蓝色选中态 SWIPELIST_SetTextColor(hList, SWIPELIST_CI_ITEM_TEXT_UNSEL, GUI_BLACK); SWIPELIST_SetTextColor(hList, SWIPELIST_CI_ITEM_TEXT_SEL, GUI_BLACK); SWIPELIST_SetTextColor(hList, SWIPELIST_CI_ITEM_HEADER_UNSEL, 0x00006080); // 深蓝色标题 SWIPELIST_SetTextColor(hList, SWIPELIST_CI_ITEM_HEADER_SEL, 0x00006080); SWIPELIST_SetFont(hList, SWIPELIST_FI_ITEM_HEADER, GUI_Font16B_1); SWIPELIST_SetFont(hList, SWIPELIST_FI_ITEM_TEXT, GUI_Font13_1); // 设置分隔项样式 SWIPELIST_SetFont(hList, SWIPELIST_FI_SEP_ITEM, GUI_Font20B_1); SWIPELIST_SetTextColor(hList, SWIPELIST_CI_SEP_ITEM_TEXT, GUI_DARKGRAY); SWIPELIST_SetBkColor(hList, SWIPELIST_CI_SEP_ITEM_BK, 0x00F0F0F0); // 浅灰色背景 // 精细的间距控制 SWIPELIST_SetBorderSize(hList, SWIPELIST_BI_LEFT, 15); SWIPELIST_SetBorderSize(hList, SWIPELIST_BI_RIGHT, 15); SWIPELIST_SetBorderSize(hList, SWIPELIST_BI_TOP, 8); SWIPELIST_SetBorderSize(hList, SWIPELIST_BI_BOTTOM, 8); SWIPELIST_SetBitmapSpace(hList, 10); // 设置分隔线为细线 SWIPELIST_SetDefaultSepSize(1); SWIPELIST_SetDefaultSepColor(GUI_LIGHTGRAY); } return hList; }掌握SWIPELIST本质上是在掌握一种在有限资源下组织信息和交互的设计模式。它提供的丰富API就像一套精细的雕刻工具多练习、多思考你就能用它打造出既流畅又专业的嵌入式设备界面。