1. 项目概述为什么需要深入了解GUIDRV_SPage在嵌入式GUI开发这条路上我踩过不少坑尤其是在显示驱动这一块。很多时候项目卡壳不是因为GUI库本身功能不够强大而是卡在了如何让图形库和那块小小的LCD屏“对上话”这个环节。emWin作为一款久经沙场的嵌入式图形库其强大之处就在于它提供了一套高度抽象的驱动框架而GUIDRV_SPage正是这套框架中针对一类特定显示控制器我们常说的“段页式”或“列行式”控制器的“翻译官”。简单来说GUIDRV_SPage驱动是emWin与众多单色或低色彩深度LCD控制器之间的桥梁。它的核心价值在于将emWin统一的图形绘制命令翻译成这些控制器能听懂的“方言”。这些控制器比如我们常见的SSD1306驱动OLED屏、ST7567、UC1611等它们管理显存的方式不是我们熟悉的“一维线性帧缓冲区”而是“页-段”结构。如果你不理解这种结构直接去写底层驱动会非常痛苦坐标换算能让你怀疑人生。GUIDRV_SPage的价值就是帮你把这些底层细节封装起来你只需要告诉它屏幕尺寸、接口方式等基本信息它就能帮你处理好所有的数据搬运和地址映射。这篇文章我会结合手册内容和实际项目经验为你彻底拆解GUIDRV_SPage。无论你是刚刚接触emWin还是在调试一块新屏幕时遇到了显示错乱、花屏、性能低下等问题相信这里的细节和“踩坑”记录都能给你带来直接的帮助。我们将从它的设计思路、支持的硬件“家谱”开始一步步深入到配置宏、缓存机制、硬件接口函数最后用一个可复现的配置实例收尾让你不仅能“配通”更能“配懂”。2. GUIDRV_SPage支持的硬件生态与核心特性解析2.1 控制器“家谱”你的屏幕在名单里吗手册里列出的控制器列表很长但我们可以将其分为几个主要的家族这有助于我们理解它们的共性和寻找替代型号。Epson S1D15xxx/S1D15Exx系列这是非常经典且广泛应用的一个系列例如S1D15705、S1D15710等常用于早期的单色点阵LCD模块。S1D15E05/E06则属于其增强型。Sitronix ST75xx/ST65xx系列另一个巨头ST7565、ST7567是驱动128x64单色LCD的“国民级”芯片成本低资料多。ST75256则支持灰度显示2bpp。ST7591相对较新功能也更丰富。Solomon后被晶门科技收购SSD13xx系列这可能是大家最熟悉的尤其是SSD1306几乎成为了0.96寸OLED屏的代名词。SSD1303、SSD1305则是其前代或变种。这个系列的指令集相对规整初始化序列也比较固定。UltraChip UC16xx/UC17xx系列如UC1601、UC1611、UC1701等在工业仪表、手持设备中很常见特点是抗干扰能力强工作温度范围宽。其他厂商如Novatek联咏、Samsung三星、Sino Wealth芯旺等也都有对应的控制器。实操心得这份列表是emWin V5.28支持的。如果你的控制器型号不在列表中并不意味着完全不能用。很多控制器是互相兼容的尤其是同系列或克隆型号。例如很多标称ST7565的屏其实用的是国产兼容芯片但指令集基本一致。此时你可以尝试使用列表中功能最接近的驱动配置如GUIDRV_SPage_Set1510()来初始化成功率很高。最保险的方法是比对你的控制器数据手册和emWin驱动源码里对应控制器的初始化代码。2.2 色彩深度与显示方向驱动配置的基石GUIDRV_SPage支持1、2、4 bpp比特每像素的色彩深度。这是一个关键限制意味着它主要面向单色或低灰度屏。1 bpp纯黑白。每个像素用1个比特表示0为灭背景色1为亮前景色。这是最常见的模式用于绝大多数单色LCD。2 bpp4级灰度。每个像素用2个比特表示可以显示4种不同的灰度等级。这对于ST75256这类控制器实现平滑的菜单过渡或反锯齿字体很有用。4 bpp16级灰度。每个像素用4个比特能显示16级灰度视觉效果更细腻但对控制器和显存都有更高要求。显示方向Orientation的配置是另一个容易让人困惑的地方。emWin通过一系列后缀宏来定义_OY: Y轴镜像垂直翻转_OX: X轴镜像水平翻转_OS: X和Y轴交换旋转90度或270度的基础这些后缀可以组合如_OSXY表示交换且镜像。手册里特别强调了一个重要提示几乎所有支持的控制器都支持硬件镜像通过发送特定的控制器指令。emWin强烈建议在控制器的初始化序列中完成镜像设置而不是依赖驱动层的软件镜像。因为软件镜像需要在每次绘制时进行坐标变换会严重消耗CPU资源降低绘制性能。这个坑我踩过在STM32F103这类资源紧张的MCU上启用软件镜像后刷屏速度肉眼可见地变慢。2.3 接口类型如何与MCU“握手”GUIDRV_SPage支持显示控制器的间接接口。所谓间接接口就是MCU通过模拟或硬件外设如FSMC、FlexBus以并行或串行方式与控制器通信而不是直接访问一块映射到内存空间的显存那种叫直接接口或8080/6800并行接口GUIDRV_SPage不支持这种。它支持三种总线形式8位并行Parallel通常使用8根数据线D0-D7加上控制线如RS, WR, RD, CS。这是速度最快的方式。4线SPI即标准的SPI接口SCLK, MOSI, MISO, CS加上额外的数据/命令选择线通常叫D/C或A0。这是最节省IO口的方式。I2C使用两根线SCL, SDA。速度最慢但连线最简单常用于小尺寸OLED。在驱动层无论底层物理接口是哪种最终都需要抽象成一组8位数据读写函数通过一个名为GUI_PORT_API的结构体传递给驱动。这就是驱动与硬件解耦的关键。3. 驱动配置详解从宏定义到运行时函数3.1 驱动选择与链接GUI_DEVICE_CreateAndLink这是使用任何emWin驱动的第一步。在你的LCD_X_Config()函数中你会看到类似这样的代码pDevice GUI_DEVICE_CreateAndLink(GUIDRV_SPAGE_1C1, GUICC_1, 0, 0);我们来拆解这个函数的四个参数驱动标识符例如GUIDRV_SPAGE_1C1。这个宏的名字就包含了全部关键信息SPAGE表示驱动类型1表示1bppC1表示使用缓存Cache。如果你选择GUIDRV_SPAGE_4C0就表示4bpp且不用缓存。颜色转换器例如GUICC_1。颜色转换器负责将emWin内部的颜色格式通常是24位RGB转换为驱动所需的颜色格式1/2/4 bpp的索引色或灰度。GUICC_1对应1bpp黑白转换GUICC_2对应2bpp灰度GUICC_4对应4bpp灰度。这里必须与驱动标识符的bpp数匹配否则颜色显示会完全错误。层号Layer和驱动索引通常设为0表示第0层单层显示和第0个驱动实例。3.2 缓存机制用内存换速度的权衡配置宏中的C0和C1就代表了是否启用显示数据缓存。缓存是一块在MCU RAM中开辟的空间大小与屏幕显存总比特数相当用于完整镜像LCD控制器的显存内容。启用缓存C1的优势大幅提升绘制速度emWin绘制时只需修改RAM中的缓存数据最后由驱动在适当时机如刷新周期一次性同步到LCD。避免了频繁、低速的LCD控制器读写操作。支持XOR等高级绘制模式某些操作如XOR异或绘制需要先读取当前像素值计算后再写入。如果没有缓存就需要对LCD控制器进行“读-改-写”操作速度极慢。缓存的存在使得这些操作都在高速RAM中完成。启用缓存的代价消耗宝贵的RAM。缓存大小计算公式为(LCD_YSIZE (8 / LCD_BITSPERPIXEL - 1)) / 8 * LCD_BITSPERPIXEL * LCD_XSIZE。以1bpp、128x64的屏幕为例(64 (8/1 -1))/8 * 1 * 128 (647)/8 * 128 71/8*128 8*128 1024字节。对于资源紧张的MCU这1KB可能就很关键。以4bpp、320x240的屏幕为例(240 (8/4 -1))/8 * 4 * 320 (2401)/8 * 4 * 320 241/8*1280 ≈ 30*1280 38400字节高达37.5KB这在很多低端MCU上是无法承受的。注意事项手册明确建议为了更快的LCD访问速度强烈推荐使用缓存。除非你的MCU RAM真的捉襟见肘或者屏幕极小否则都应该启用缓存。我的经验是在Cortex-M0/M3这类芯片驱动128x64单色屏时启用缓存带来的流畅度提升是质变的。但对于更高分辨率的灰度屏必须仔细计算RAM占用。3.3 运行时配置函数精细调校显示创建驱动设备后需要通过一系列运行时函数对其进行配置。这些函数主要分为三类1. 基础配置GUIDRV_SPage_Config()这个函数传递一个CONFIG_SPAGE结构体目前主要包含两个参数FirstSEG控制器显存中使用的起始段列地址。通常为0。某些屏幕的物理像素阵列可能不是从显存的0列开始这时需要调整。这个值需要查阅具体的LCD模块数据手册或者通过实验试错确定。FirstCOM控制器显存中使用的起始行Common地址。通常也为0。作用同上。2. 硬件接口配置GUIDRV_SPage_SetBus8()这是最关键的一步它将驱动与你的硬件底层连接起来。你需要实现一个GUI_PORT_API结构体并填充其中的函数指针。这个结构体要求你提供4个函数pfWrite8_A0向控制器写一个字节此时A0或叫D/C线为低表示写入的是命令。pfWrite8_A1向控制器写一个字节此时A0线为高表示写入的是数据。pfWriteM8_A1向控制器连续写入多个字节数据A1状态。这是一个优化函数用于快速填充区域。如果你不实现驱动会循环调用pfWrite8_A1效率较低。pfRead8_A1从控制器读取一个字节数据A1状态。如果启用了缓存C1这个函数可以置为NULL因为所有读操作都从缓存进行。如果不启用缓存则必须实现。你需要根据你的硬件连接GPIO模拟、SPI、I2C来实现这四个函数。例如对于4线SPIstatic void _Write8_A0(U8 Data) { LCD_CS_LOW(); LCD_A0_LOW(); // 命令 SPI_SendByte(Data); LCD_CS_HIGH(); } static void _Write8_A1(U8 Data) { LCD_CS_LOW(); LCD_A0_HIGH(); // 数据 SPI_SendByte(Data); LCD_CS_HIGH(); } static void _WriteM8_A1(U8 *pData, int NumItems) { LCD_CS_LOW(); LCD_A0_HIGH(); for (int i 0; i NumItems; i) { SPI_SendByte(pData[i]); } LCD_CS_HIGH(); } static U8 _Read8_A1(void) { U8 data; LCD_CS_LOW(); LCD_A0_HIGH(); data SPI_ReceiveByte(); // 假设你的SPI支持全双工 LCD_CS_HIGH(); return data; }3. 控制器特定配置GUIDRV_SPage_SetXXXX()这是一组函数用于针对不同的控制器家族进行特定的初始化设置。例如GUIDRV_SPage_Set1510()配置一大类控制器包括SSD1306, ST7567等。GUIDRV_SPage_SetUC1611()专门配置UC1611控制器。GUIDRV_SPage_SetST75256()专门配置ST752562bpp灰度。这些函数内部会向LCD控制器发送一系列特定的初始化命令来设置驱动电压、偏置比、对比度、扫描方向等。你必须根据你实际使用的LCD模块型号调用正确的函数。调用错误的函数可能导致屏幕无法点亮、显示错乱或对比度异常。4. 完整配置流程与实战代码剖析下面我将结合一个实际项目中最常见的场景使用STM32的硬件SPI驱动一块128x64、1bpp、控制器为SSD1306的OLED屏来展示完整的配置流程。4.1 硬件抽象层实现首先我们需要实现底层的硬件读写函数。假设我们使用STM32的SPI1并定义了控制引脚。// lcd_hardware.c #include lcd_hardware.h #include spi.h // 你的SPI驱动 // 引脚定义 #define LCD_CS_PIN GPIO_PIN_4 #define LCD_CS_PORT GPIOA #define LCD_DC_PIN GPIO_PIN_5 // A0/D/C 引脚 #define LCD_DC_PORT GPIOA #define LCD_RES_PIN GPIO_PIN_6 // 复位引脚可选 #define LCD_RES_PORT GPIOA static void LCD_WriteByte(uint8_t data, uint8_t is_data) { // 设置D/C线 HAL_GPIO_WritePin(LCD_DC_PORT, LCD_DC_PIN, is_data ? GPIO_PIN_SET : GPIO_PIN_RESET); // 拉低片选 HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_RESET); // SPI发送数据 HAL_SPI_Transmit(hspi1, data, 1, HAL_MAX_DELAY); // 拉高片选 HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_SET); } // GUI_PORT_API 要求的函数 void _Write8_A0(U8 Data) { LCD_WriteByte(Data, 0); // 写命令 } void _Write8_A1(U8 Data) { LCD_WriteByte(Data, 1); // 写数据 } void _WriteM8_A1(U8 *pData, int NumItems) { HAL_GPIO_WritePin(LCD_DC_PORT, LCD_DC_PIN, GPIO_PIN_SET); // 数据模式 HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_RESET); HAL_SPI_Transmit(hspi1, pData, NumItems, HAL_MAX_DELAY); HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_SET); } U8 _Read8_A1(void) { U8 data 0; // 注意SSD1306在4线SPI模式下通常不支持读操作。 // 如果启用缓存(C1)此函数不会被调用可以返回一个虚拟值。 // 如果未启用缓存(C0)则必须实现读操作可能需要切换为3线SPI或检查控制器是否支持。 return data; }4.2 驱动配置函数 LCD_X_Config这是emWin要求用户实现的配置函数是整个显示驱动的核心。// LCDConf.c #include GUI.h #include GUIDRV_SPage.h // 假设屏幕物理尺寸 #define XSIZE_PHYS 128 #define YSIZE_PHYS 64 extern void _Write8_A0(U8 Data); extern void _Write8_A1(U8 Data); extern void _WriteM8_A1(U8 *pData, int NumItems); extern U8 _Read8_A1(void); void LCD_X_Config(void) { GUI_PORT_API PortAPI {0}; CONFIG_SPAGE Config {0}; GUI_DEVICE * pDevice; // // 第1步创建并链接显示驱动设备 // 使用1bpp启用缓存以获得最佳性能 // pDevice GUI_DEVICE_CreateAndLink(GUIDRV_SPAGE_1C1, GUICC_1, 0, 0); // // 第2步配置显示尺寸 // 这里我们使用默认方向不交换XY // LCD_SetSizeEx (0, XSIZE_PHYS, YSIZE_PHYS); // 设置物理显示尺寸 LCD_SetVSizeEx(0, XSIZE_PHYS, YSIZE_PHYS); // 设置虚拟显示尺寸通常与物理尺寸相同 // // 第3步驱动基础配置 // 对于大多数SSD1306模块起始地址都是0 // Config.FirstSEG 0; Config.FirstCOM 0; GUIDRV_SPage_Config(pDevice, Config); // // 第4步配置硬件接口函数 // 将我们实现的硬件函数赋值给PortAPI结构体 // PortAPI.pfWrite8_A0 _Write8_A0; PortAPI.pfWrite8_A1 _Write8_A1; PortAPI.pfWriteM8_A1 _WriteM8_A1; PortAPI.pfRead8_A1 _Read8_A1; // 注意因为我们用了C1缓存此函数实际不会被调用 GUIDRV_SPage_SetBus8(pDevice, PortAPI); // // 第5步控制器特定配置 // SSD1306属于Solomon系列应使用Set1510 // GUIDRV_SPage_Set1510(pDevice); // // 第6步可选但重要执行控制器硬件初始化 // GUIDRV_SPage_Set1510()只配置了驱动内部参数还需要发送上电、复位、对比度等命令。 // 这部分通常在系统启动时调用LCD_X_Init()函数完成。 // }4.3 控制器硬件初始化 LCD_X_InitGUIDRV_SPage_Set1510()等函数主要配置了驱动内部与控制器通信的“协议”但控制器本身的硬件初始化如上电序列、偏置设置、对比度等需要额外完成。emWin通常建议在LCD_X_Init()函数中实现。// LCDConf.c (续) void LCD_X_Init(void) { // 1. 硬件复位如果连接了RESET引脚 HAL_GPIO_WritePin(LCD_RES_PORT, LCD_RES_PIN, GPIO_PIN_RESET); HAL_Delay(10); // 保持低电平至少一段时间 HAL_GPIO_WritePin(LCD_RES_PORT, LCD_RES_PIN, GPIO_PIN_SET); HAL_Delay(10); // 等待复位完成 // 2. 发送SSD1306初始化命令序列 // 这些命令来源于SSD1306数据手册不同模块可能略有差异 _Write8_A0(0xAE); // 关闭显示 _Write8_A0(0xD5); // 设置显示时钟分频比/振荡器频率 _Write8_A0(0x80); // 建议值 _Write8_A0(0xA8); // 设置多路复用比率 _Write8_A0(0x3F); // 对于64行屏幕是0x3F _Write8_A0(0xD3); // 设置显示偏移 _Write8_A0(0x00); // 无偏移 _Write8_A0(0x40); // 设置显示起始行行0 _Write8_A0(0x8D); // 电荷泵设置 _Write8_A0(0x14); // 启用内部电荷泵对于OLED必须 _Write8_A0(0x20); // 设置内存地址模式 _Write8_A0(0x00); // 水平地址模式 _Write8_A0(0xA1); // 段重映射设置A1: 列地址127映射到SEG0即水平翻转 _Write8_A0(0xC8); // 扫描方向设置C8: 从COM[N-1]到COM0即垂直翻转 _Write8_A0(0xDA); // 设置COM引脚硬件配置 _Write8_A0(0x12); // 对于64行屏幕是0x12 _Write8_A0(0x81); // 设置对比度控制 _Write8_A0(0xCF); // 对比度值 (0x00~0xFF) _Write8_A0(0xD9); // 设置预充电周期 _Write8_A0(0xF1); // 建议值 _Write8_A0(0xDB); // 设置VCOMH电压等级 _Write8_A0(0x40); // 建议值 _Write8_A0(0xA4); // 关闭整体显示点亮 _Write8_A0(0xA6); // 设置正常显示非反色 _Write8_A0(0xAF); // 开启显示 // 3. 清屏可选 GUI_Clear(); }关键细节解析注意初始化序列中的0xA1和0xC8命令。它们分别控制了段重映射和扫描方向这直接对应了emWin驱动配置中的_OX和_OY硬件镜像。如果你希望屏幕的显示方向与emWin的逻辑坐标一致可能需要调整这两个值。例如如果你的屏幕上下颠倒了可以将0xC8改为0xC0。这就是手册强调的“在控制器初始化中完成镜像”远比使用驱动的软件镜像宏高效。5. 常见问题排查与性能优化技巧5.1 问题排查速查表在实际项目中配置GUIDRV_SPage时遇到的问题五花八门。下面这个表格总结了我遇到过的典型问题及解决思路问题现象可能原因排查步骤与解决方案屏幕全白/全黑无任何显示1. 电源或背光问题。2. 硬件复位失败。3. 初始化命令序列错误或未执行。4. 控制器选择函数如Set1510调用错误。1. 用万用表测量屏的VCC、GND电压检查背光电路。2. 用逻辑分析仪或示波器抓取SPI波形确认LCD_X_Init中的命令序列是否被正确发送。3. 核对数据手册确保初始化命令序列与你的LCD模块完全匹配。不同厂家的模块初始化命令可能有细微差别。4. 确认调用的GUIDRV_SPage_SetXXXX()函数与你的控制器型号匹配。显示内容错乱、花屏、有规律条纹1. 显示尺寸LCD_SetSizeEx设置错误。2.FirstSEG或FirstCOM设置错误。3. 色彩深度bpp不匹配。4. 驱动标识符与颜色转换器不匹配。5. 显存数据组织方式理解错误。1. 确认XSIZE_PHYS和YSIZE_PHYS与屏幕实际分辨率一致。2. 尝试调整FirstSEG和FirstCOM通常为0但有些屏从非0开始。3. 确认屏幕控制器支持1/2/4bpp并与GUIDRV_SPAGE_xCx中的x一致。4. 确保GUI_DEVICE_CreateAndLink中驱动标识符的bpp数与颜色转换器GUICC_x匹配。5. 仔细阅读控制器数据手册中“显存组织”章节理解其页、列对应关系。显示方向不对旋转/镜像1. 硬件扫描方向与软件配置不匹配。2. 同时使用了硬件命令和软件镜像宏导致冲突。1. 优先在LCD_X_Init中使用控制器的命令如SSD1306的0xA1/0xA0,0xC8/0xC0调整方向。2. 如果使用硬件命令调整后方向正确则不要在驱动创建时使用_OX,_OY等软件镜像宏。绘制速度极慢1. 未启用缓存使用了C0宏。2.pfWriteM8_A1函数未实现或实现效率低下。3. SPI时钟频率设置过低。4. 在未启用缓存时使用了需要读显存的操作如XOR。1.首要检查确认使用的是GUIDRV_SPAGE_xC1宏。2. 实现高效的pfWriteM8_A1函数利用MCU的DMA或硬件SPI的连续发送功能。3. 将SPI时钟提升到控制器允许的最高频率通常10MHz以上。4. 避免在无缓存模式下使用复杂绘制模式或改为启用缓存。启用缓存后MCU内存不足缓存占用RAM过大。1. 使用前面给出的公式精确计算缓存大小。2. 考虑换用更高bpp的屏幕是否必要1bpp屏最省内存。3. 优化工程中其他部分的RAM使用或更换RAM更大的MCU。编译错误找不到GUIDRV_SPage相关符号emWin库未包含SPage驱动或配置宏未正确定义。1. 确认购买的emWin授权或使用的版本包含GUIDRV_SPage驱动。2. 在LCDConf.h中确认包含了正确的驱动头文件并正确设置了GUI_DEVICE_CreateAndLink的宏。5.2 性能优化与高级技巧1. 最大化pfWriteM8_A1的效率这个函数是批量数据传输的关键。对于SPI接口务必使用MCU的硬件SPIDMA。一个高效的实现能比单字节循环快一个数量级。void _WriteM8_A1_DMA(U8 *pData, int NumItems) { LCD_DC_HIGH(); LCD_CS_LOW(); HAL_SPI_Transmit_DMA(hspi1, pData, NumItems); // 需要等待DMA传输完成信号这里简化处理 while (HAL_SPI_GetState(hspi1) ! HAL_SPI_STATE_READY); LCD_CS_HIGH(); }2. 精细控制缓存与显存同步启用缓存后emWin会在内部管理缓存与真实显存的同步。但你可以通过GUI_Exec()函数来触发一次同步实际上GUI_Exec()会处理所有待处理的绘制请求和缓存刷新。在需要确保显示内容立即更新的关键操作后如更新重要数据可以手动调用GUI_Exec()。3. 处理非标准分辨率和偏移有些LCD模块的物理像素点并非从控制器显存的(0,0)开始。例如一个132x65的控制器驱动一个128x64的屏可能需要在两侧留出2个像素的边距。这时就需要调整FirstSEG可能设为2和LCD_SetSizeEx设为128, 64。这需要仔细阅读模块规格书。4. 动态切换显示方向虽然不常见但有些应用需要旋转屏幕。由于硬件镜像命令在初始化时发送动态切换较复杂。一种可行方案是创建两个不同的驱动设备如GUIDRV_SPAGE_1C1和GUIDRV_SPAGE_OS_1C1并分别用不同的硬件初始化序列配置然后在运行时通过GUI_DEVICE_Delete()和GUI_DEVICE_CreateAndLink()切换。但这会涉及缓存数据的重建实现起来较繁琐。5. 调试利器使用模拟器SEGGER提供了emWin的Windows模拟器。在移植到硬件之前可以先用模拟器验证你的GUI应用逻辑和显示尺寸是否正确。这能极大节省在硬件上调试布局和功能的时间。最后我想说的是GUIDRV_SPage驱动虽然针对的是相对“古老”和简单的控制器但它在低资源嵌入式设备中依然有着不可替代的地位。吃透它的配置逻辑不仅能让你快速点亮一块屏幕更能让你深刻理解emWin驱动框架的设计哲学——通过抽象和分层将复杂的硬件差异封装起来为上层应用提供稳定统一的图形接口。当你下次遇到一块陌生的LCD屏时翻出它的数据手册对照着GUIDRV_SPage的支持列表和配置步骤相信你也能从容应对。