MPC8XXFADS评估板硬件调试实战:从BCSR配置到内存控制器与UPM时序详解
1. 项目概述与核心价值在嵌入式系统开发领域硬件调试平台是连接软件算法与物理电路的关键桥梁其重要性不亚于软件调试器。MPC8XXFADSPowerPC架构评估板正是这样一个为硬件工程师和底层驱动开发者量身打造的专业工具。它不是一块简单的“开发板”而是一个集成了完整硬件验证环境的工程平台其核心价值在于将复杂的PowerPC处理器、内存子系统、通信接口和可编程逻辑单元整合在一个可观测、可配置的系统中。我接触过不少评估板很多要么过于简单只提供基本功能要么过于封闭关键信号无法引出。MPC8XXFADS的设计思路很明确既要提供一个“开箱即用”的完整系统又要保证所有关键信号从地址总线到调试接口都能被开发者触及和测量。这种设计哲学体现在其丰富的扩展连接器、逻辑分析仪接口以及核心的BCSR板级控制与状态寄存器上。通过BCSR你可以在运行时动态启用或禁用板载的Flash、DRAM、SDRAM、以太网、串口等模块这为硬件调试带来了极大的灵活性。比如当你需要验证自己设计的外部存储器接口时可以简单地通过写BCSR寄存器来禁用板载内存将对应的芯片选择CS信号释放到扩展接口从而无缝接入你的硬件进行测试。这块板子特别适合那些从事工业控制、网络通信设备、高性能嵌入式网关开发的工程师。如果你正在基于MPC8xx系列如MPC821, MPC823, MPC850, MPC860等进行产品设计那么MPC8XXFADS能帮你快速验证处理器初始化代码、内存控制器配置尤其是复杂的UPM时序、设备驱动如SCC用于以太网和UART以及系统级的软硬件协同工作。它缩短了从原理图设计到系统稳定运行之间的漫长调试周期让你能把精力更多集中在产品本身的创新上而不是反复折腾硬件底层的兼容性问题。2. 硬件架构深度解析与设计思路MPC8XXFADS的硬件设计体现了模块化和可扩展性的核心思想。其架构可以清晰地分为几个层次以PowerPC MPC8xx处理器为核心的计算单元、由内存控制器管理的存储子系统、丰富的外设通信接口、灵活的电源与时钟管理以及至关重要的调试与配置子系统。2.1 核心处理器与时钟系统板载的MPC8xx处理器具体型号取决于子卡是整个系统的大脑。设计上支持从MPC801到MPC860T等多个型号这通过可更换的子卡Daughter Board实现。这种模块化设计非常聪明它让一块主板能适配一个处理器家族降低了用户的硬件成本和学习曲线。时钟源的选择通过一个拨码开关DS2/4进行提供了两种模式一是来自板载4MHz时钟发生器的1:5 PLL倍频模式二是来自32.768kHz晶振的1:513倍频模式。选择低频晶振模式通常是为了降低功耗或满足特定的时序测试需求。这里有一个容易被忽略的细节时钟模式的选择不仅切换了输入源还通过硬件逻辑切换了连接在处理器XFC引脚上的环路滤波电容5nF对应1:5模式0.68µF对应1:513模式这是确保锁相环PLL稳定工作的关键。如果自行设计电路这个细节必须注意错误的电容值会导致时钟抖动甚至无法锁相。2.2 存储子系统灵活性与性能的平衡存储子系统是评估板的重点也是硬件调试中最常打交道的地方。MPC8XXFADS提供了三种类型的存储器均由处理器的内存控制器管理Flash SIMM采用标准SIMM插座支持2MB到8MB容量的JEDEC标准Flash模块如MCM29F系列。其创新之处在于仅使用一个芯片选择信号CS0~通过板载可编程逻辑在U22中实现解码Flash SIMM上的“在位检测”Presence-Detect引脚自动识别模块的容量和速度并内部生成多个片选信号给Flash芯片的各内部Bank。这意味着你在更换不同容量的Flash时无需改动硬件跳线软件通过读取BCSR2中的FLASH_PD位就能知道当前插的是什么芯片并据此配置BR0和OR0寄存器。这种设计极大地提升了硬件的通用性。DRAM SIMM同样采用SIMM插座支持4MB到32MB、60/70ns的FP或EDO DRAM。其数据总线宽度可通过BCSR1中的Dram_Half_Word位配置为32位或16位。当设置为16位模式时硬件会通过多路复用器切换地址线A10和A30的连接见图4-2以维持正确的字节对齐。这里有个重要提示如果你在代码运行时动态切换DRAM宽度务必确保这段切换代码不在DRAM自身中执行否则会导致总线访问错误和系统崩溃。稳妥的做法是将切换代码放在Flash或Cache中运行。板载SDRAM直接焊接了4MB的同步DRAM如MB811171622A。与DRAM使用UPM用户可编程状态机控制不同SDRAM使用另一个UPMUPMB控制并且为了追求更高性能其地址和数据线是直接连接到处理器总线的未经过缓冲器。这减少了延迟但也意味着SDRAM的负载直接加在处理器引脚上在设计自己的扩展板时需要注意总线负载。所有存储器模块都可以通过BCSR1独立启用或禁用。禁用后对应的芯片选择信号CS0~, CS2~, CS3~, CS4~会呈高阻态可供扩展板使用。同时板上的数据收发器也会在对应区域被禁用防止总线冲突。这是一个非常实用的安全设计确保了你在连接自定义内存设备时不会与板载内存发生数据总线竞争。2.3 通信接口与外设控制板载通信接口涵盖了嵌入式系统的常见需求双串口RS-232使用MC145707收发器支持软件关断通过BCSR1的RS232EN1~和RS232EN2~位。关断后对应的SCC引脚可用于其他功能。以太网10-Base-T基于MC68160 EEST物理层芯片同样支持软件关断。快速红外Fast IrDA基于TFDS6000支持最高4Mbps速率。其速率范围9600-1.2Mbps 或 1.2-4Mbps由使能信号IrdEn~下降沿时的TX引脚状态决定这是一个硬件配置点。PCMCIA插槽完全符合2.1标准支持5V卡。通过LTC1315芯片管理电源VCC和VPP支持热插拔。插入检测CD1~,CD2~和电压检测VS1,VS2引脚都引到了连接器软件必须读取这些状态后才能上电否则有损坏3.3V卡的风险。调试端口ADI通过一个专用控制器U7基于MACH220 CPLD将处理器的JTAG调试口转换为并行的ADI接口方便连接主机进行源码级调试。所有这些外设的使能、配置状态都汇聚到BCSR板级控制与状态寄存器。BCSR本质上是一个由CPLDU11实现的、映射在处理器内存空间基址0x02100000的寄存器组。通过读写BCSR软件可以像操作普通内存一样控制硬件开关、查询板卡信息如内存型号、子卡版本。这种将硬件控制“软件化”的思路是MPC8XXFADS作为高效调试平台的核心。2.4 电源、复位与调试架构电源设计考虑了扩展需求除了给板卡自身供电的5V、3.3V、2V核心电压和12VFlash编程总线外这些电源也被引到了扩展连接器可供子卡使用并标注了最大输出电流如3.3V可达2A。复位电路支持多种源上电复位、手动硬复位/软复位、处理器内部复位以及通过调试接口发起的复位。特别是硬复位配置字可以从BCSR0或Flash的首字中取得这为不同的启动场景提供了灵活性。调试系统是另一大亮点。除了标准的JTAG接口板载的ADI调试控制器允许通过简单的并口电缆与主机通信。更强大的是通过一个10针的接头P5你可以将MPC8XXFADS本身变成一个“调试探头”去调试另一个目标系统。使用时有一个至关重要的警告在此模式下必须将主板上的处理器芯片移除否则两个处理器的调试数据输出线DSDO会发生冲突导致硬件损坏。这个功能对于调试自定义的硬件原型机极其有用。3. 核心实操上电、配置与内存控制器调试拿到一块硬件平台第一步就是让它“跑起来”。对于MPC8XXFADS这个过程涉及物理连接、电源配置、启动模式选择和最关键的内存控制器初始化。3.1 硬件准备与上电安装内存板卡出厂时DRAM和Flash SIMM模块是单独包装的。首先需要将它们安装到对应的SIMM插槽72针和80针上。注意缺口方向对准后以一定角度插入然后扳直锁紧卡扣。连接电源通过接线端子P6连接5V电源最大5A如果需要对12V可编程Flash或PCMCIA卡编程还需连接P7的12V电源最大1A。建议使用稳压电源并确保地线连接良好。设置启动模式通过DIP开关DS2选择调试器地址用于多板调试和时钟源。通常如果通过ADI调试时钟源选择4MHzDS2-4 OFF即可。连接调试器使用37芯扁平电缆将板卡的P1ADI口连接到主机内的ADI卡。如果使用独立调试器则通过10针电缆连接P5。连接串口如果需要控制台输出将串口线连接到PA2或PB29针D型母头。3.2 BCSR寄存器详解与操作示例BCSR是软件与硬件交互的枢纽。理解其各个字段是进行任何高级调试的基础。它分为多个寄存器BCSR0-BCSR4每个寄存器有特定功能。访问它们就像访问内存地址例如在C代码中#define BCSR_BASE 0x02100000 typedef volatile struct { uint32_t bcsr0; // 硬复位配置字 uint32_t bcsr1; // 主控制寄存器 uint32_t bcsr2; // 状态寄存器内存类型等 uint32_t bcsr3; // 状态/控制寄存器子卡ID等 uint32_t bcsr4; // 外设控制寄存器以太网模式等 } BCSR_t; BCSR_t *bcsr (BCSR_t *)BCSR_BASE;关键寄存器操作示例禁用板载Flash释放CS0~供外部使用// 首先必须解除BCSR_EN位的写保护 // BCSR_EN的保护位在BCSR3的bit 5 (CNT_REG_EN_PROTECT~) // 向BCSR3的bit 5写入1解除保护注意此位是只写的读回无意义 *((uint32_t*)(BCSR_BASE 0x0C)) (1 5); // 写BCSR3地址偏移0x0C // 然后清除BCSR1的bit 0 (FLASH_EN~) 来禁用Flash // 同时确保BCSR_EN (bit 6) 为0启用并重新置位保护位向BCSR1写操作会自动置位保护 bcsr-bcsr1 ~(1 0); // bit00 禁用Flash其他位保持默认bit60启用BCSR // 此时CS0~信号不再驱动板载Flash可以用于外部设备。注意这个操作有风险。一旦禁用BCSR自身BCSR_EN置1你将无法再通过内存映射访问BCSR寄存器只能通过重新上电恢复。所以操作时要格外小心。查询当前安装的DRAM信息uint32_t status bcsr-bcsr2; uint8_t dram_type (status 5) 0x0F; // 提取DRAM_PD(4:1)位 switch(dram_type) { case 0x0: printf(4MB SIMM (MCM36100/MT8D132X)\n); break; case 0x3: printf(8MB SIMM (MCM36200)\n); break; case 0x2: printf(16MB SIMM (MCM36400/MT8D432X)\n); break; case 0x1: printf(32MB SIMM (MCM36800/MT16D832X)\n); break; default: printf(Unknown or no DRAM\n); } uint8_t dram_speed (status 3) 0x03; // 提取DRAM_PD(4:3)位 if(dram_speed 0x03) printf(Speed: 60ns\n); else if(dram_speed 0x02) printf(Speed: 70ns\n);3.3 内存控制器初始化从理论到实践MPC8xx的内存控制器非常强大但也相对复杂。它通过基址寄存器BRx、选项寄存器ORx和用户可编程状态机UPM来管理不同类型的存储器。文档中给出了针对50MHz和25MHz系统时钟的详细配置表表3-3表3-6等但这些值不是魔法数字理解其计算逻辑才能应对不同的内存芯片或频率。以配置一个60ns的EDO DRAM SIMM在50MHz下工作为例确定内存基址和大小假设我们使用CS2区域从地址0x00000000开始大小为4MB0x00400000。那么BR2应设置为0x00000081基址0端口大小32位UPM模式使能。计算OR2值选项寄存器定义内存块的掩码、访问类型和时序。地址掩码AM对于4MB块地址线A22-A29用于块选择。掩码值计算为~(Size - 1)。4MB 0x00400000Size-1 0x003FFFFF。取反在OR的AM字段是取反后的值的高位部分结合手册中OR的格式对于4MBAM字段应设置为0xFFC00000中的相关位。在给出的例子里OR2被设置为0xFFC00800。0xFFC00000是掩码0x00000800定义了其他选项如SCY1个时钟的等待周期。等待周期对于50MHz周期20ns访问60ns的DRAM理论需要3个等待周期60ns/20ns。但UPM控制提供了更精细的时序控制实际配置可能不同。配置UPM RAM这是最复杂的部分。UPM RAM存储了一系列微代码用于生成精确的DRAM控制信号RAS、CAS、WE序列。文档表3-5提供了60ns EDO DRAM在50MHz下的UPM数组值。例如单次读操作的微代码在偏移0x00-0x07偏移0: 0x8FFFEC24 偏移1: 0x0FFFEC04 偏移2: 0x0CFFEC04 偏移3: 0x00FFEC04 ...这些32位值每一位对应一个控制信号在特定时钟周期的输出值。你需要按照MPC手册的UPM编程指南将这些值写入UPM RAM通过MCR寄存器。一个常见的坑是忘记在初始化后执行MCR命令来启动UPM序列加载。配置刷新通过MPTPR周期定时器预分频和MAMRUPM A模式寄存器设置刷新间隔。计算公式文档中已给出核心是确保在规定的刷新周期内如64ms完成所有行的刷新。例如对于1024行、4个突发刷新、BRG时钟20ns、预分频16计算出的PTA值约为970x61。初始化代码片段示例void init_sdram_50mhz(void) { // 1. 配置UPMB RAM数组示例为SDRAM模式寄存器设置MRS uint32_t upm_b_mrs[] {0x1FF77C34, 0xEFAABC34, 0x1FA57C35, ...}; // 来自表3-9 // 将数组写入UPMB RAM地址0x0000 - 0x00FF for(int i0; isizeof(upm_b_mrs)/4; i) { memctl-memc_mdr upm_b_mrs[i]; memctl-memc_mcr (0x0000 | i) 16 | 0x00000001; // 命令写入UPM RAM } // 2. 配置基址/选项寄存器 memctl-memc_br4 0x030000C1; // 基址0x03000000 UPMB 使能 memctl-memc_or4 0xFFC00A00; // 4MB块其他选项 // 3. 配置MAMR (UPMB模式寄存器) memctl-memc_mbmr 0xD0802114; // 刷新分频等设置 // 4. 执行SDRAM初始化序列预充电、多个刷新、模式寄存器设置 // a. 预充电所有bank memctl-memc_mar 0x00000000; // 地址位A101表示预充电所有bank memctl-memc_mcr 0x80802104; // 运行UPMB中预充电命令假设在位置0x04 // b. 执行8个自动刷新周期 for(int i0; i8; i) { memctl-memc_mcr 0x80802130; // 运行UPMB中刷新命令位置0x30 } // c. 设置模式寄存器 memctl-memc_mar 0x00000088; // 模式寄存器值CAS延迟2突发长度4 memctl-memc_mcr 0x80802105; // 运行UPMB中MRS命令位置0x05 // d. 恢复正常刷新率4个突发 memctl-memc_mbmr 0xC0802114; }实操心得在调试UPM时序时逻辑分析仪是你的最佳伙伴。抓取CSx~、RAS~、CAS~、WE~、地址和数据总线信号与内存芯片数据手册中的时序图对比能快速定位是建立时间、保持时间还是预充电时间不满足要求。文档中提供的UPM数组是经过验证的起点但如果更换了内存芯片或调整了系统频率你可能需要微调这些值。4. 外设接口调试与常见问题排查硬件调试不仅是让内存工作还要让各种外设正常通信。MPC8XXFADS上的外设大多通过SCC串行通信控制器或SMC串行管理控制器连接并由BCSR控制其物理层收发器的使能。4.1 串口SCC2 SMC1调试板载两个RS-232串口分别可能映射到SCC2和SMC1具体取决于处理器型号和引脚复用配置。首先确保在BCSR1中使能了对应的RS232ENx~位让收发器上电。常见问题1无输出或乱码检查时钟串口波特率依赖于BRG波特率发生器时钟。确认BRGCLK的来源和频率通常等于系统时钟或分频。计算波特率分频器时确保系统时钟频率设置正确。检查引脚复用MPC8xx的引脚功能是复用的。必须正确配置SIUMCR和SYPCR等寄存器将对应引脚设置为SCC或SMC的UART功能而不是普通I/O或其他复用功能。检查电平转换虽然板载MC145707完成了电平转换但检查其VCC5V是否正常。可以用万用表测量串口连接器上的TX引脚在空闲时应为负电压约-5V到-12V。配置示例SCC2作为UART 115200波特率系统时钟50MHz// 假设BRG时钟为50MHz void init_scc2_uart(void) { // 1. 配置引脚复用参考处理器手册例如将PD3, PD4设置为SCC2的UART immr-im_ioport.iop_pdpar | 0x0018; // PD3, PD4 设置为外设功能 immr-im_ioport.iop_pddir ~0x0018; // 方向由外设控制 // 2. 配置SCC2为UART模式 immr-im_scc[2].scc_gsmr_h 0x00000000; immr-im_scc[2].scc_gsmr_l 0x00000010; // 透明模式 UART immr-im_scc[2].scc_psmr 0x0400; // 8位数据无校验 // 3. 配置波特率BRG2 // CD (BRGCLK / (16 * 波特率)) - 1 // CD (50,000,000 / (16 * 115200)) - 1 ≈ 26.1 - 取整26 immr-im_brgc[2] 0x0001001A; // 使能BRG2 CD26 // 4. 使能收发器 immr-im_scc[2].scc_sccm 0x0027; // 使能所有UART相关中断可选 immr-im_scc[2].scc_gsmr_l | 0x00000002; // 使能发送 immr-im_scc[2].scc_gsmr_l | 0x00000001; // 使能接收 }4.2 以太网SCC1与LXT970快速以太网调试对于MPC860T子卡快速以太网通过LXT970物理层芯片连接。调试分为两部分SCC控制器配置和LXT970物理层配置。常见问题2网络链路不通检查物理连接和指示灯首先观察板载的以太网指示灯LINK, RX, TX。如果LINK灯不亮检查网线、对端设备以及LXT970的硬件配置通过DIP开关DS1设置自协商、双工模式等。检查BCSR4配置ETHLOOP位用于环回测试TPFLDL~用于设置全双工TPSQEL~用于冲突测试。确保这些位设置正确通常正常工作时ETHLOOP0禁用环回TPFLDL~0允许全双工。检查SCC的NMSI/TSA配置以太网需要将SCC连接到NMSI非复用串行接口或TSA时分交换架构并正确配置引脚。对于MPC860T通常涉及CMXSCR等寄存器的配置将SCC连接到特定的TDM时隙或NMSI引脚。初始化LXT970通过MII管理接口MDC/MDIO配置LXT970的内部寄存器。这包括软件复位、设置自协商能力、选择速度/双工模式等。务必在SCC初始化前完成PHY的配置。LXT970 MII初始化片段void lxt970_phy_init(int phy_addr) { // 1. 软件复位PHY miiphy_write(phy_addr, MII_BMCR, BMCR_RESET); mdelay(100); // 等待复位完成 // 2. 设置自协商能力100Base-TX全双工和半双工 10Base-T全双工和半双工 miiphy_write(phy_addr, MII_ADVERTISE, ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10FULL | ADVERTISE_10HALF | ADVERTISE_CSMA); // 3. 重启自协商 miiphy_write(phy_addr, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART); // 4. 等待自协商完成 uint16_t bmsr; int timeout 5000; // 5秒超时 while(timeout--) { miiphy_read(phy_addr, MII_BMSR, bmsr); if(bmsr BMSR_ANEGCOMPLETE) break; mdelay(1); } if(!(bmsr BMSR_ANEGCOMPLETE)) { printf(PHY auto-negotiation failed!\n); // 可以尝试强制模式例如强制100M全双工 // miiphy_write(phy_addr, MII_BMCR, BMCR_SPEED100 | BMCR_FULLDPLX); } else { printf(PHY auto-negotiation completed.\n); // 读取链路状态寄存器确认速度和双工模式 uint16_t lpa; miiphy_read(phy_addr, MII_LPA, lpa); if(lpa LPA_100FULL) printf(100Mbps Full Duplex\n); else if(lpa LPA_100HALF) printf(100Mbps Half Duplex\n); // ... 其他状态 } }4.3 PCMCIA接口调试要点PCMCIA接口的调试相对复杂因为它涉及电源管理、卡检测和复杂的访问协议。常见问题3无法识别PCMCIA卡电源和使能首先确认BCSR1中的PCCEN~位为0使能并根据卡的类型通过VS1,VS2引脚读取正确设置PCCVCC0和PCCVCC1位来提供5V或3.3V VCC。警告在读取VSx引脚前切勿给卡座供电卡检测插入卡后CD1~和CD2~引脚应变低。软件应轮询或中断检测这两个引脚的状态变化。属性内存访问在访问卡的常规I/O或内存空间前通常需要先访问其属性内存CIS卡信息结构来识别卡的类型和配置。这需要将REG_A~信号拉低。时序配置MPC8xx的PCMCIA控制器时序可通过PIPR端口中断引脚分配、PBRx和PORx等寄存器配置。如果访问超时或失败检查这些寄存器的设置是否满足卡的数据手册要求特别是建立、保持和等待时间。4.4 调试端口ADI问题排查如果无法通过ADI接口连接调试器如CodeWarrior或单机调试工具检查物理连接确保37芯电缆连接牢固主机ADI卡安装正确。检查地址设置确认板卡上的DS2开关1-3位设置的从机地址与调试器软件中设置的地址一致。检查终端电阻某些ADI总线需要终端电阻请参考ADI卡手册。检查处理器状态如果处理器因错误配置如错误的内存时序而“锁死”可能无法响应调试命令。此时可以尝试按下板上的硬复位按钮SW1和SW2同时按下在复位期间连接。使用“目标系统调试”模式如果怀疑是主板处理器问题可以移除处理器芯片将主板作为调试探头连接到你的目标板进行测试这能隔离问题。5. 高级技巧与避坑指南基于多年的调试经验这里分享一些在MPC8XXFADS上容易踩坑的地方和进阶技巧电源序列虽然MPC8xx对电源序列不敏感但良好的习惯是先上I/O电源3.3V再上核心电源2V或3.3V。关断时顺序相反。板卡设计通常已处理但使用自定义扩展板时需注意。未用输入引脚MPC8xx有许多复用引脚。对于未使用的输入功能特别是中断请求IRQx~务必在软件中禁用或在硬件上拉防止其悬空引起意外中断或功耗增加。UPM编程的时序裕量文档中的UPM数组是针对特定频率和内存型号优化的。当你更换更快或更慢的内存或者调整系统频率后需要重新计算时序。重点检查tRCDRAS到CAS延迟、tCASCAS延迟、tRP预充电时间和tRC行周期时间是否满足。在时序紧张的情况下如高频率可以尝试减少SCY片选到输出有效等参数但要以逻辑分析仪波形为准。SDRAM初始化的严格顺序SDRAM上电后必须严格按照1) 等待稳定期通常200µs、2) 预充电所有Bank、3) 执行至少8个通常8个自动刷新周期、4) 加载模式寄存器MRS、5) 进入正常操作模式。任何步骤的缺失或顺序错误都会导致SDRAM工作不稳定。利用BCSR进行“软”故障注入这是该平台的一大优势。你可以编写测试程序动态地禁用/启用某些模块如关闭DRAM后测试Flash的单独访问或者模拟错误条件如通过BCSR4强制以太网进入环回模式进行协议栈测试。这种可控性对驱动开发和系统稳定性测试非常宝贵。散热考虑MPC8xx系列在50MHz全速运行时会产生一定热量。如果放在密闭环境或长时间满负荷运行建议增加散热片。过热可能导致内存访问出现偶发性错误。保存你的配置一旦调试出一套稳定的内存和外设参数BR/OR/UPM数组、波特率等建议将其保存在Flash的固定区域或者作为你项目BSP板级支持包的一部分。这能保证每次上电或复位后都有一个已知良好的起点。MPC8XXFADS是一个功能强大的平台其价值在于它暴露了足够多的硬件细节供你探索和控制。从理解BCSR的每一位含义到手动微调UPM时序以满足苛刻的SDRAM要求这个过程本身就是对嵌入式硬件核心技术的深度实践。当你能够熟练地让这块板子上的所有资源协同工作时你对PowerPC架构乃至整个嵌入式硬件系统的理解将会达到一个新的层次。记住硬件调试没有捷径耐心、细致的测量示波器/逻辑分析仪和对数据手册的反复研读是成功的关键。