深入解析Cortex-M4系统控制寄存器:从原理到RTOS与低功耗实战
1. 项目概述与核心价值
在嵌入式开发的深水区,尤其是基于ARM Cortex-M4这类高性能内核的项目里,系统控制寄存器(System Control Registers)是连接硬件底层与软件逻辑的“神经中枢”。很多开发者,特别是从Arduino或简单单片机项目过渡过来的朋友,往往习惯于依赖厂商提供的库函数,对HAL_Init()或SystemInit()背后究竟做了什么,总感觉隔着一层毛玻璃。直到某天,你需要实现一个极其苛刻的低功耗应用,或者调试一个只在特定中断嵌套下才出现的诡异死机问题时,才会发现,不理解这些寄存器,就像医生不懂解剖学,只能凭经验开药,遇到疑难杂症就束手无策。
我手头这份关于Tiva™ TM4C1294NCPDT微控制器的寄存器文档,恰好为我们提供了一个绝佳的“解剖”样本。它详细列出了从VTABLE到FAULTSTAT等一系列关键系统控制寄存器。这些寄存器绝非冰冷的位域定义,它们直接决定了你的系统如何启动、如何响应突发事件、如何在错误中自救,以及如何在空闲时“沉睡”以节省每一微安电流。理解它们,意味着你从“库函数调用者”转变为“系统架构师”,能够精准地控制芯片的每一个核心行为。无论是为实时操作系统(RTOS)定制高效的中断管理策略,还是为电池供电的物联网设备设计毫瓦级功耗方案,这些知识都是你工具箱里最锋利的解剖刀。
2. 核心寄存器深度解析与设计思路
Cortex-M4内核通过一套精密的系统控制块(System Control Block, SCB)来管理其核心行为。Tiva™系列微控制器作为Cortex-M4的具体实现,其SCB寄存器映射在固定的地址0xE000E000起始的区域。我们看到的VTABLE、APINT等寄存器,都是这个SCB的组成部分。它们的配置,共同构建了系统的异常处理框架、优先级仲裁逻辑和电源管理策略。
2.1 异常向量表重定位:VTABLE寄存器
为什么需要重定位向量表?这是第一个要回答的“为什么”。在默认情况下,Cortex-M4从地址0x00000000开始获取向量表。但对于复杂的应用,尤其是使用了Bootloader或者将程序放在外部Flash/ RAM执行的场景,0x00000000可能并不存放我们的主应用程序。Bootloader需要有自己的向量表,应用程序也需要自己的。VTABLE寄存器(地址偏移0xD08)就是为了解决这个矛盾而生的。
关键位域解析:
- OFFSET[31:10] (RW): 向量表基地址的偏移量。这是核心字段。注意它的单位和对齐要求。文档明确指出:“偏移必须与向量表中的异常条目数量对齐。因为有112个中断,偏移必须对齐在1024字节边界上。” 这句话信息量很大。
- 计算过程:Cortex-M4的向量表包含内核异常(如复位、NMI、硬错误等)和外部中断。假设芯片支持112个外部中断,那么向量表总条目数 = 16个内核异常 + 112个外部中断 = 128个条目。每个条目是一个4字节的地址指针。因此,向量表总大小为 128 * 4 = 512字节。但为什么要求1024字节(1KB)对齐?这是ARM架构为了硬件寻址效率而设定的对齐约束,通常要求对齐到大于或等于表大小的下一个2的整数次幂边界。512字节的下一个2的幂是1024。因此,你设置的OFFSET值,其低10位(
[9:0])必须为0。例如,如果你想将向量表重定位到0x20010000,你需要计算OFFSET = 0x20010000 - 0x00000000 = 0x20010000。然后检查其低10位是否为0。0x20010000 & 0x3FF = 0,符合要求。写入VTABLE寄存器的值就是0x20010000。
- 计算过程:Cortex-M4的向量表包含内核异常(如复位、NMI、硬错误等)和外部中断。假设芯片支持112个外部中断,那么向量表总条目数 = 16个内核异常 + 112个外部中断 = 128个条目。每个条目是一个4字节的地址指针。因此,向量表总大小为 128 * 4 = 512字节。但为什么要求1024字节(1KB)对齐?这是ARM架构为了硬件寻址效率而设定的对齐约束,通常要求对齐到大于或等于表大小的下一个2的整数次幂边界。512字节的下一个2的幂是1024。因此,你设置的OFFSET值,其低10位(
- reserved[9:0] (RO): 保留位,读为0。软件必须保持其值不变(在读写操作中保留原值),以确保与未来产品的兼容性。
实操心得与注意事项:
注意:对VTABLE寄存器的写操作必须在特权模式下进行。在RTOS中,通常只在系统初始化阶段(处于特权级的线程模式或Handler模式)配置一次。如果应用程序运行在非特权模式(用户线程),尝试修改VTABLE会导致一个用法错误(Usage Fault)。
常见误区:不要忘记在重定位向量表后,更新你的链接脚本(如
.ld文件),确保向量表区域(通常是一个名为.isr_vector的段)被正确放置到你指定的新基地址。否则,你配置的偏移地址指向的将是一片无效或随机的内存,系统启动或发生异常时会立即跑飞。
2.2 中断与复位总控:APINT寄存器
APINT寄存器(地址偏移0xD0C)是一个功能集成的关键寄存器,主要管理三件事:中断优先级分组、数据端序和系统复位。
1. 中断优先级分组(PRIGROUP[10:8])这是理解Cortex-M4嵌套向量中断控制器(NVIC)优先级的关键。Cortex-M4使用8位来表示一个中断的优先级,但并非所有位都用于抢占。PRIGROUP字段将这8位(实际上高4位或全部8位,取决于实现,此处为高3位[7:5])划分为组优先级(Group Priority)和子优先级(Subpriority)。
- 组优先级:决定中断的抢占能力。高组优先级的中断可以抢占低组优先级的中断。
- 子优先级:在组优先级相同的中断之间,决定响应顺序。子优先级高的先响应,但它不能导致抢占。
文档中的表格是理解这一切的钥匙:
| PRIGROUP值 | 二进制点位置 | 组优先级字段 | 子优先级字段 | 组优先级数 | 子优先级数 |
|---|---|---|---|---|---|
| 0x0 - 0x4 | bxxx. | [7:5] | 无 | 8 | 1 |
| 0x5 | bxx.y | [7:6] | [5] | 4 | 2 |
| 0x6 | bx.yy | [7] | [6:5] | 2 | 4 |
| 0x7 | b.yyy | 无 | [7:5] | 1 | 8 |
- 解读:以
PRIGROUP=0x5为例,它表示将3位优先级([7:5])在第二位之后画一个“二进制点”,点左边2位([7:6])是组优先级,可以有4个级别(0-3);点右边1位([5])是子优先级,可以有2个级别(0-1)。一个优先级值为0x82(二进制1000 0010,取高3位100即4)的中断,其组优先级为10b(2),子优先级为0b(0)。 - 配置逻辑:选择哪种分组,取决于你的系统需求。如果你需要很多能相互抢占的中断级别,就选择组优先级多的配置(如
PRIGROUP=0x0,8组)。如果你需要更多在同一抢占级别内的仲裁顺序,就选择子优先级多的配置(如PRIGROUP=0x7,8个子级)。在RTOS中,通常将内核异常(如PendSV、SysTick)和关键外设中断设为较高的组优先级,而将多个非关键、且不应相互抢占的中断设为同一组优先级、不同的子优先级。
2. 写保护钥匙(VECTKEY[31:16])这是一个重要的安全特性。要修改APINT寄存器的任何位(除了只读的ENDIANESS),你必须先向VECTKEY字段写入魔法数字0x05FA。这个16位的值必须与0xFA05(复位值)组合成一个32位写入操作。通常的写法是:SCB->AIRCR = (0x5FA << 16) | (new_value)。如果钥匙不对,写入操作会被硬件静默忽略。这有效防止了程序跑飞时意外修改这些关键配置。
3. 系统复位请求(SYSRESREQ[2])这是一个只写(WO)位。向该位写1会请求一个系统复位,复位整个内核和片上外设(调试接口除外)。这个功能常用于看门狗超时后的软件复位,或者在固件升级后发起热复位。它与直接操作芯片复位引脚不同,是一种由软件发起的、干净的内核级复位。
4. 调试相关位(VECTCLRACT[1]和VECTRESET[0])这两个位标记为“保留用于调试”,并且必须写0,否则行为不可预测。在正常的应用编程中,你永远不应该去触碰它们。它们是给调��器(如JTAG/SWD)在单步调试、复位内核时使用的。
2.3 低功耗模式入口控制:SYSCTRL寄存器
SYSCTRL寄存器(地址偏移0xD10)控制着处理器如何进入和退出低功耗状态,是实现超低功耗应用的核心。
1. 睡眠深度选择(SLEEPDEEP[2])这是决定睡得多“深”的关键。
- 0 (默认):选择睡眠(Sleep)模式。在此模式下,仅关闭处理器内核(Cortex-M4)的时钟,但所有外设、内存和时钟源(如PLL)仍然运行。唤醒速度快,功耗降低有限。
- 1:选择深度睡眠(Deep-Sleep)模式。在此模式下,不仅关闭处理器时钟,还可以根据芯片具体设计,关闭主时钟源(如PLL)、闪存、SRAM等。功耗显著降低,但唤醒需要更长时间,因为系统时钟可能需要重新稳定。
2. 睡眠退出行为(SLEEPEXIT[1])这个位控制从中断服务程序(ISR)返回时的行为。
- 0 (默认):从Handler模式(中断)返回到Thread模式(主程序或任务)时,处理器正常继续执行,不进入睡眠。
- 1:从Handler模式返回到Thread模式时,处理器立即进入由SLEEPDEEP决定的睡眠模式。这被称为“中断驱动的睡眠”。这对于事件驱动的低功耗应用至关重要。你的主循环
main()可能是一个空的while(1),当没有任何事件(中断)发生时,系统停留在睡眠模式。一旦中断发生,ISR处理完毕后,由于此位被置1,处理器自动再次入睡,无需软件在main循环中显式调用睡眠指令。
3. 待定中断唤醒(SEVONPEND[4])此位影响WFE(等待事件)指令的行为。
- 0 (默认):只有已启用的中断或事件才能将处理器从
WFE指令唤醒。 - 1:任何中断进入挂起状态(即使该中断在NVIC中被禁用),都能将处理器从
WFE指令唤醒。这允许你使用一个被禁用的中断作为纯粹的“事件”信号来唤醒系统,而不触发其ISR。这在多核通信或复杂的电源管理序列中很有用。
低功耗模式设计思路:一个典型的低功耗应用流程是:初始化后,设置SLEEPDEEP=1(深度睡眠),SLEEPEXIT=1(中断返回即睡)。在主循环中,配置所有需要唤醒系统的中断(如GPIO边沿、定时器、串口接收),然后执行WFI(等待中断)或WFE指令进入睡眠。当唤醒中断发生后,ISR处理事件,ISR返回后由于SLEEPEXIT=1,系统自动再次进入睡眠。整个应用逻辑完全由中断驱动。
2.4 系统配置与控制:CFGCTRL寄存器
CFGCTRL寄存器(地址偏移0xD14)包含一些杂项但非常重要的系统级配置。
1. 栈对齐(STKALIGN[9])Cortex-M4内核要求栈指针(SP)在异常入口时8字节对齐。如果此位为0,处理器会在异常入口时自动调整SP以实现8字节对齐,并在栈上记录对齐前的SP值(通过PSR的第9位)。如果此位为1,则假定软件已经保证了8字节对齐,处理器不再进行自动调整。强烈建议保持此位为1(复位值),并确保你的启动代码和RTOS将主栈和进程栈初始化为8字节对齐。不对齐的栈访问可能导致数据错误或性能下降。
2. 故障陷阱使能(DIV0[4]和UNALIGNED[3])
- DIV0:置1时,执行除数为0的
SDIV或UDIV指令会触发一个用法错误(Usage Fault)。置0时,除0操作会静默地返回0作为商。在开发阶段,强烈建议使能此陷阱,以便快速捕获潜在的算法错误。 - UNALIGNED:置1时,非对齐的半字或字访问(如对奇地址进行
LDRH加载)会触发用法错误。置0时,硬件会自动处理非对齐访问(可能以性能为代价)。对于追求最高性能和确定性的实时系统,建议使能此陷阱,强制软件进行对齐的内存访问。
3. 忽略总线错误(BFHFNMIGN[8])这是一个高级调试功能。置1时,运行在优先级-1(硬错误)或-2(NMI)的异常处理程序,将忽略由加载/存储指令引起的数据总线错误。这允许调试器或极其健壮的错误处理程序去探测有问题的内存映射设备(如外设桥接器),而不会因为总线错误导致立即锁定(Lockup)。警告:只有当你确信处理程序及其数据位于绝对安全的内存(如片上SRAM)中时,才能使用此功能。普通应用程序绝不应设置此位。
2.5 系统异常优先级与状态控制
SYSPRI1-3和SYSHNDCTRL寄存器共同管理着Cortex-M4内核的“内置”异常(也称为系统异常)。
1. 优先级配置(SYSPRI1, SYSPRI2, SYSPRI3)这些寄存器允许你配置Usage Fault、Bus Fault、MemManage Fault、SVCall、SysTick、PendSV和Debug Monitor的优先级。它们的优先级是可编程的(通常0-7,数字越小优先级越高)。配置策略:
- 硬错误(HardFault):优先级固定为-1(最高),不可配置。
- NMI:优先级固定为-2(次高),不可配置。
- MemManage, BusFault, UsageFault:通常设置为较高的优先级(如0或1),以便及时捕获严重的内存或指令错误。
- SVCall(SVC指令):系统调用入口,其优先级应高于普通应用任务但低于关键错误,在RTOS中需仔细设计。
- PendSV和SysTick:这是RTOS的“心脏”和“节拍”。通常将它们设置为最低的可配置优先级(如7)。这样,它们可以被任何外设中断抢占,确保系统的实时响应性。PendSV用于上下文切换,SysTick用于提供时间片。
2. 使能与状态监控(SYSHNDCTRL)这个寄存器功能强大且危险。
- 使能位(USAGE[18], BUS[17], MEM[16]):用于启用或禁用对应的可配置错误异常。如果禁用后发生了此类错误,则会升级(Escalate)为硬错误。在开发初期,建议全部使能,以便获得最详细的错误信息。在产品发布时,出于鲁棒性考虑,可能会禁用某些非关键的错误检测。
- 挂起位(SVC[15], BUSP[14], MEMP[13], USAGEP[12])和活动位(TICK[11], PNDSV[10], ... MEMA[0]):这些位反映了异常的当前状态,并且软件可以写它们来改变状态。这是实现高级操作系统功能的关键。
- 挂起位:你可以手动置位一个异常的挂起位来“触发”该异常。例如,在RTOS中,通过置位PendSV的挂起位来请求一次延迟的上下文切换。
- 活动位:表示该异常处理程序当前是否正在执行。文档中的警告(Caution)必须高度重视:操作系统内核在进行上下文切换时,可能需要修改这些活动位,以反映当前正在执行的异常。但是,如果修改不当(例如,没有同步更新栈上的上下文),将立即导致新的错误。普通应用编程绝不要手动修改活动位。
3. 故障诊断与状态解析:FAULTSTAT寄存器
当系统发生MemManage、BusFault或UsageFault时,FAULTSTAT寄存器(地址偏移0xD28)是你进行“尸检”的第一现场。它是一个“写1清除”的寄存器,每个位都指示了具体的错误原因。
寄存器结构:它分为三个子状态寄存器:
- MFAULTSTAT[7:0]:内存管理错误状态。如指令/数据访问违规(IERR, DERR)、栈操作错误(MSTKE, MUSTKE)。
- BFAULTSTAT[15:8]:总线错误状态。如精确/不精确数据错误(PRECISE, IMPRE)、指令总线错误(IBUS)、栈操作错误(BSTKE, BUSTKE)。
- UFAULTSTAT[31:16]:用法错误状态。如未定义指令(UNDEF)、非法状态(INVSTAT)、无效PC加载(INVPC)、无协处理器(NOCP)、非对齐访问(UNALIGN)、除零(DIV0)。
诊断流程与实操要点:
- 第一时间保存现场:在错误处理程序(如UsageFault_Handler)中,首先读取并保存相关的故障地址寄存器(MMADDR或FAULTADDR)。
- 检查地址有效性:接着,读取FAULTSTAT中的
MMARV或BFARV位,确认刚才保存的地址是否有效。这个顺序至关重要,因为一个更高优先级的中断可能会抢占当前的错误处理程序,并覆盖这些地址寄存器。 - 解析错误类型:仔细检查FAULTSTAT中的各个状态位。例如,
PRECISE位为1表示是一个精确的数据总线错误,并且FAULTADDR中保存了确切的故障地址。而IMPRE位为1表示不精确错误,你无法直接定位到是哪条指令导致的,这通常与写缓冲(Write Buffer)有关,调试起来更困难。 - 栈错误(MSTKE/BSTKE, MUSTKE/BUSTKE):这些位指示错误发生在异常压栈(Entry)或出栈(Return)过程中。当它们被置位时,栈指针(SP)可能已被调整,但栈上的内容可能是错误的,且故障地址寄存器无效。这通常意味着栈溢出或栈指针被破坏,是系统崩溃的常见原因。
- 清除状态位:在分析完错误信息后,通过向相应的状态位写1来清除它们,为下一次错误记录做好准备。
一个真实的调试案例:我曾遇到一个系统,在某个复杂的中断嵌套场景下随机进入硬错误。在HardFault_Handler中,我检查SCB->HFSR(硬错误状态寄存器),发现原因是“被升级的错误”。然后我回溯检查CFSR(可配置错误状态寄存器,即FAULTSTAT),发现BUSP和IMPRE位被置1,但BFARV为0。这表明一个不精确的总线错误被触发,并因为其优先级低于当前执行的中断而处于挂起状态,最终因为无法得到服务而升级为硬错误。由于是不精确错误,没有故障地址。最终通过排查,发现是一个DMA操作在访问一个尚未完成初始化的外部SDRAM控制器时产生了错误,而这个错误是异步的(不精确)。解决方法是在启动DMA前,确保外设时钟和初始化序列已完成。
4. 系统控制寄存器的综合应用与避坑指南
理解了单个寄存器后,如何将它们串联起来,构建一个健壮、高效的系统?这里分享一些综合性的配置策略和常见陷阱。
4.1 启动流程中的关键配置序列
一个典型的、不依赖库函数的启动代码(startup.s或早期C初始化)应包含以下对系统控制寄存器的操作:
- 初始化栈指针和VTABLE:从Flash的起始位置(通常是0x00000000)加载主栈指针(MSP)的初始值,然后根据你的内存布局,如果需要,立即配置VTABLE寄存器。如果你的应用程序从0x00000000开始运行,此步可省略。
- 配置中断优先级分组:尽早通过APINT寄存器的PRIGROUP字段设置你期望的优先级分组方案。这必须在使能任何中断之前完成。
- 配置系统异常优先级:通过SYSPRI1-3寄存器,设置SysTick、PendSV、SVC以及各类Fault的优先级。在RTOS中,通常将SysTick和PendSV设为最低优先级。
- 使能需要的系统异常:通过SYSHNDCTRL寄存器,使能MemManage、BusFault、UsageFault。在开发阶段务必全部打开。
- 配置故障陷阱:通过CFGCTRL寄存器,使能除零(DIV0)和非对齐访问(UNALIGNED)陷阱,帮助在开发阶段捕获错误。
- 配置低功耗:根据应用需求,配置SYSCTRL寄存器,特别是SLEEPEXIT位,为后续的低功耗管理打下基础。
4.2 在RTOS中的核心作用
对于像FreeRTOS、µC/OS-III这样的RTOS,系统控制寄存器是它们得以运行的基石:
- PendSV与SysTick:SysTick提供系统时钟节拍,其异常优先级被设为最低。当需要任务切换时,RTOS内核不会直接进行耗时的上下文切换,而是置位PendSV的挂起位。由于PendSV也被设为最低优先级,当前正在执行的中断服务程序会继续执行完毕。在所有更高优先级中断都处理完后,PendSV服务程序才会执行,进行实际的上下文切换。这种设计最小化了中断延迟。
- SVCall:用于实现从用户任务(线程模式)到内核服务(处理器模式)的安全调用。用户任务通过SVC指令发起系统调用(如创建任务、释放信号量),触发SVCall异常,在特权模式下执行内核代码。
- 故障处理:RTOS需要强大的故障处理机制。通过配置UsageFault等,RTOS可以捕获任务中的非法操作(如非法内存访问、除零),并可能选择性地终止该任务,而不是导致整个系统崩溃。
4.3 常见问题排查速查表
| 现象 | 可能相关的寄存器/位域 | 排查思路 |
|---|---|---|
| 系统上电后无法启动,或跑飞 | VTABLE (OFFSET) | 检查VTABLE偏移地址计算是否正确,是否满足对齐要求(低10位为0)。检查链接脚本,确认向量表是否确实被链接到了该地址。 |
| 中断无法嵌套,或嵌套行为不符合预期 | APINT (PRIGROUP) | 确认中断优先级分组(PRIGROUP)设置是否符合你的设计。检查每个中断的优先级值(组优先级和子优先级)是否在NVIC中正确设置。记住,只有组优先级更高的中断才能抢占当前中断。 |
| 进入低功耗模式后无法被中断唤醒 | SYSCTRL (SLEEPEXIT, SEVONPEND) | 1. 确认进入睡眠前,已使能了目标中断(在NVIC中)。 2. 如果使用 WFE,检查SEVONPEND位,如果为0,则只有已使能的中断能唤醒。3. 确认唤醒中断的优先级足够高(如果当前处于Handler模式)。 4. 检查 SLEEPEXIT是否已置1,以确保中断返回后能再次自动睡眠。 |
| 程序偶尔死机,触发硬错误 | FAULTSTAT, MMADDR/FAULTADDR | 1. 在硬错误处理程序中,首先读取HFSR寄存器,查看原因(如FORCED位)。2. 读取 CFSR(即FAULTSTAT)和对应的故障地址寄存器。3. 分析具体错误位(如 DERR,PRECISE,MSTKE等)。栈错误(*STKE)通常是栈溢出或栈指针被野指针修改。 |
| 除零操作未触发错误 | CFGCTRL (DIV0) | 确认CFGCTRL寄存器的DIV0位已被置1。默认情况下,除零不会触发异常。 |
| 手动置位PendSV挂起位,但未触发上下文切换 | SYSHNDCTRL (PNDSV pending bit) | 1. 确认PendSV异常已在NVIC中使能。 2. 确认当前中断屏蔽寄存器(如PRIMASK)未屏蔽所有中断。 3. 确认你写入的是PendSV的挂起位(bit 10),并且使用的是正确的“写1置位”操作(如`SCB->ICSR |
| 修改APINT寄存器(如请求复位)无效 | APINT (VECTKEY) | 任何对APINT的写操作,都必须将0x05FA写入高16位(VECTKEY字段)。确保你的操作是类似SCB->AIRCR = (0x5FAUL << 16) | (1UL << 2)(请求复位)这样的格式。 |
4.4 高级技巧与心得
- 利用FAULTSTAT进行内存保护单元(MPU)调试:当你启用MPU来保护内存区域时,任何违规访问都会触发MemManage Fault。此时,FAULTSTAT中的
IERR或DERR位会置1,并且MMADDR寄存器会保存违规访问的地址(如果MMARV为1)。这是验证MPU配置是否正确的最直接方法。 - 软件复位与看门狗的结合:除了使用APINT的
SYSRESREQ进行软件复位,更常见的做法是利用独立看门狗(IWDG)或窗口看门狗(WWDG)。但SYSRESREQ在需要立即、干净地复位整个系统(如固件升级后)时非常有用。可以将两者结合:看门狗用于捕获程序跑飞,而SYSRESREQ用于计划内的复位。 - 深度睡眠下的外设状态保持:进入深度睡眠(
SLEEPDEEP=1)前,务必查阅芯片数据手册,了解哪些外设时钟会被关闭,哪些寄存器状态会丢失。你可能需要手动保存/恢复关键外设的上下文,或者将唤醒源配置为能在深度睡眠下工作的模式(如低功耗定时器、外部引脚边沿)。 - 保留位的处理:所有寄存器中的保留位(reserved),在读写时都必须遵循“读-修改-写”原则,即先读取整个寄存器,修改你需要改的位,然后写回。确保保留位的值在写回时保持不变。这是保证代码在未来兼容其他型号芯片的好习惯。
深入理解并熟练运用Cortex-M4的系统控制寄存器,是嵌入式工程师从初级迈向高级的必经之路。它让你不再对芯片的行为感到神秘,而是能够像指挥家一样,精准地控制这支硬件乐队的每一个声部。当系统出现问题时,这些寄存器提供的状态信息就是你最好的侦探工具。花时间研读手册,动手实验,将这些知识内化,你的调试效率和系统设计能力必将获得质的飞跃。