深入解析TI MibSPI关键寄存器:从DMA配置到RAM奇偶校验实战

📅 2026/7/23 19:14:58 👁️ 阅读次数 📝 编程学习
深入解析TI MibSPI关键寄存器:从DMA配置到RAM奇偶校验实战

1. 项目概述:从寄存器位域到系统可靠性的桥梁

在嵌入式系统,尤其是汽车电子和工业控制这类对实时性与可靠性要求严苛的领域,与外设的每一次数据交换都容不得半点差错。我们常常与SPI(串行外设接口)打交道,用它来读取传感器数据、配置外围芯片或与存储器通信。标准的SPI模块虽然简单直接,但在处理连续、高速或复杂序列的数据流时,往往需要CPU频繁介入,效率低下且难以保证时序的确定性。这时,像TI Hercules系列微控制器中集成的MibSPI(Multi-buffered SPI)模块就成了我们的“利器”。它不仅仅是一个增强型SPI,更是一个集成了专用RAM、复杂状态机和丰富控制逻辑的通信协处理器。

理解MibSPI,核心在于吃透其控制寄存器多缓冲RAM的配置。这不仅仅是照着手册填几个十六进制数那么简单,而是理解一套硬件如何被“编程”以执行复杂任务的过程。寄存器中的每一个位域,都像是一个开关或旋钮,直接控制着硬件的物理行为。比如,DMACNTLEN寄存器决定了DMA传输的“量程”,是使用精简计数器还是大容量计数器;UERRCTRLUERRSTAT等寄存器则构建了一套内存数据完整性监控系统,通过奇偶校验来捕捉潜在的硬件软错误。这些机制共同作用,将我们从繁琐的轮询和错误处理中解放出来,让SPI通信变得既高效又可靠。本文将从一线开发者的视角,深入解析MibSPI几个关键控制寄存器的工作原理、配置要点和实战中的“坑”,目标是让你不仅能配置,更能理解为什么这么配置,从而在自家的项目中游刃有余。

2. 核心机制深度解析:不止于数据传输

在深入寄存器细节之前,我们需要先建立对MibSPI核心工作机制的宏观认知。这有助于理解后续每个寄存器位域设计的初衷。

2.1 多缓冲RAM:数据与控制的“集散中心”

传统SPI通常只有一个或两个数据缓冲区(TXBUF/RXBUF),数据搬移的节奏完全由CPU或基础DMA控制。MibSPI的革命性在于引入了一块专用的多缓冲RAM。你可以把它想象成一个高度结构化的内部仓库,而非简单的临时堆放点。

这块RAM被划分为TXRAM(发送区)和RXRAM(接收区),每个区最多可包含128个“货位”(即缓冲区,具体数量依芯片型号而定)。每个货位不是一个简单的32位数据,而是一个32位的结构化字。以TXRAM的一个缓冲区为例,其32位被划分为几个关键字段:

  • 低16位 (TXDATA): 存放待发送的数据。
  • 高16位 (控制字段): 包含了本次传输的所有“调度信息”,如芯片选择号(CSNR)、数据格式(DFSEL)、缓冲区模式(BUFMODE)、时钟延迟(WDEL)等。

这种设计的精妙之处在于**“控制与数据绑定”**。当你为一次SPI传输准备好数据时,同时也将这次传输的所有参数(用哪个片选、什么时钟格式、传输后是否保持片选有效等)一并写入。MibSPI的硬件序列器(Sequencer)会按顺序自动读取这些“任务包”,并完整地执行整个通信序列,无需CPU在每次传输前重新配置SPI格式寄存器。这对于需要循环扫描多个传感器(每个传感器可能需要不同的时钟极性和相位)的应用来说,效率是颠覆性的。

2.2 传输组与序列器:硬件自动化调度

多缓冲RAM中的缓冲区可以被灵活地分组,形成多个传输组。每个TG可以独立配置其包含的缓冲区范围、触发方式和优先级。序列器则像一个硬件的调度器,按照TG的优先级和触发条件,自动从对应的缓冲区中取出“任务包”执行。

这就引出了BUFMODE这个关键字段。它定义了序列器遇到一个缓冲区时的行为策略,是MibSPI灵活性的核心。例如:

  • 跳过模式:如果缓冲区未就绪(如发送数据未更新或接收缓冲区未读),序列器直接跳过它,继续检查下一个。这适用于非关键或周期性更新的数据。
  • 挂起模式:如果缓冲区未就绪,序列器会暂停在这个缓冲区,直到条件满足。这保证了关键数据被及时、不丢失地发送或接收,是实现严格实时性的基础。
  • 连续模式:无论缓冲区状态如何,序列器都会执行传输。这通常用于产生连续的时钟或测试信号。

理解这些底层机制后,我们再去看那些控制寄存器,就会明白它们是如何为这套复杂的自动化系统设置规则、报告状态和处理异常的。

3. 关键控制寄存器详解与实战配置

下面我们聚焦于输入资料中提到的几个关键寄存器,拆解其每一位的含义,并给出典型的配置场景和代码示例。

3.1 DMA大计数寄存器:管理大数据块传输的“开关”

DMACNTLEN寄存器虽然只有1个有效位(LARGE_COUNT),但它决定了DMA传输计数器的“工作模式”,直接影响着大数据量传输的便利性。

位域解析:

  • LARGE_COUNT (位0):
    • 0:选择DMAXCTRL寄存器中的计数器。此模式下,DMA传输计数(ICOUNT)是DMAXCTRL寄存器中的一个字段。它的位宽较小(通常是8位或10位),最大计数值有限。特别注意:在此模式下,对独立的DMAXCOUNT寄存器的写入是无效的,会被后续对DMAXCTRL的写入覆盖。
    • 1:选择独立的DMAXCOUNT寄存器作为计数器。此模式下,DMA传输计数由位宽更大的独立寄存器(如16位)控制,支持更大的单次传输数据量。关键操作顺序:必须先向DMAXCOUNT寄存器写入所需的计数值,然后再设置DMAXCTRL寄存器中的RXDMAENA或TXDMAENA位来启动DMA。

配置场景与实操: 假设我们需要通过MibSPI的DMA接收一个长度为1024字节的数据流。如果使用小计数器模式(LARGE_COUNT=0),计数值可能只有8位(最大值255),这意味着我们需要配置DMA进行多次传输,或者使用自动重载等复杂逻辑。而启用大计数模式后,我们可以直接设置一个16位的计数值1024,DMA硬件即可自动完成整个数据块的搬移,大大简化了软件负担。

// 示例:配置MibSPI1的接收DMA使用大计数模式,传输1024个数据 // 假设 MibSPI1 的 DMA 相关寄存器基址已定义 #define MIBSPI1_DMA_RX_COUNT (*(volatile uint32_t*)(0xFF0E0000 + 0xXX)) // RX DMA计数寄存器地址 #define MIBSPI1_DMA_RX_CTRL (*(volatile uint32_t*)(0xFF0E0000 + 0xYY)) // RX DMA控制寄存器地址 #define MIBSPI1_DMACNTLEN (*(volatile uint32_t*)(0xFF0E0000 + 0x118)) // DMA大计数寄存器 // 步骤1:启用大计数模式 MIBSPI1_DMACNTLEN = 0x00000001; // 设置LARGE_COUNT位为1 // 步骤2:在大计数模式下,必须先配置DMAXCOUNT寄存器 MIBSPI1_DMA_RX_COUNT = 1024; // 设置需要传输的数据量 // 步骤3:最后再使能DMA控制寄存器中的接收DMA使能位 // 假设DMAXCTRL中RXDMAENA是位0,且其他位已配置好(如触发源、地址等) MIBSPI1_DMA_RX_CTRL |= 0x00000001; // 使能接收DMA

注意:这个操作顺序至关重要。如果先使能了DMA(设置了RXDMAENA),再修改DMAXCOUNT,在某些硬件实现中可能无法生效,或者导致不可预知的传输计数。

3.2 多缓冲RAM奇偶校验控制与状态寄存器:构建数据安全网

在安全至上的应用中,内存数据的完整性必须得到保障。MibSPI的多缓冲RAM集成了硬件奇偶校验功能,UERRCTRLUERRSTATUERRADDR0/1这几个寄存器共同构成了一个错误检测与定位系统。

3.2.1 不可纠正奇偶错误控制寄存器

UERRCTRL用于启用和测试奇偶校验逻辑。

  • EDEN (位[3:0]):错误检测使能。必须写入0xA(二进制1010) 来启用奇偶错误检测逻辑。默认值0x5是禁用状态。这个“密钥”值的设计是为了防止因软件跑飞或单粒子效应导致的意外位翻转而错误地关闭了保护功能。
  • PTESTEN (位8):奇偶测试使能。置1时,RAM的奇偶校验位会被映射到外设RAM地址空间,允许CPU直接读取和写入,用于生产测试或诊断。正常运行时必须保持为0

3.2.2 不可纠正奇偶错误状态与地址寄存器

当硬件检测到RAM读取时发生奇偶错误(即数据位与存储的奇偶位不匹配),会触发以下动作:

  1. UERRSTAT寄存器中的对应错误标志位被置1。
    • EDFLG0:TXRAM发生奇偶错误。
    • EDFLG1:RXRAM发生奇偶错误。
  2. 发生错误时的内存地址被锁存到对应的地址寄存器中。
    • UERRADDR0:锁存TXRAM错误地址(偏移范围 0x000-0x1FF)。
    • UERRADDR1:锁存RXRAM错误地址(偏移范围 0x200-0x3FF)。
  3. 该地址寄存器被“冻结”,直到被CPU读取。读取地址寄存器的操作会自动清除对应的EDFLG状态位。这是一个“读-清除”机制。

实战配置与错误处理流程

// 初始化时启用奇偶校验 #define MIBSPI1_UERRCTRL (*(volatile uint32_t*)(0xFF0E0000 + 0x120)) MIBSPI1_UERRCTRL = 0x0000000A; // 设置EDEN=0xA,启用错误检测,PTESTEN=0 // 在应用的中断服务程序或主循环错误检查中处理奇偶错误 #define MIBSPI1_UERRSTAT (*(volatile uint32_t*)(0xFF0E0000 + 0x124)) #define MIBSPI1_UERRADDR0 (*(volatile uint32_t*)(0xFF0E0000 + 0x12C)) #define MIBSPI1_UERRADDR1 (*(volatile uint32_t*)(0xFF0E0000 + 0x128)) void CheckParityError(void) { uint32_t status = MIBSPI1_UERRSTAT; uint32_t error_addr; if (status & 0x00000001) { // 检查EDFLG0 (TXRAM错误) error_addr = MIBSPI1_UERRADDR0; // 读取地址,同时清除EDFLG0标志 // 记录错误:error_addr是TXRAM内的偏移地址 // 可以触发安全响应,如系统复位、切换到安全状态、记录故障码等 LogError(TXRAM_PARITY_ERROR, error_addr); } if (status & 0x00000002) { // 检查EDFLG1 (RXRAM错误) error_addr = MIBSPI1_UERRADDR1; // 读取地址,同时清除EDFLG1标志 // 记录错误:error_addr是RXRAM内的偏移地址 // 接收数据可能已损坏,应丢弃该数据包并请求重传 LogError(RXRAM_PARITY_ERROR, error_addr); // 例如,如果知道是哪个缓冲区的数据,可以将其标记为无效 } }

重要提示:这些寄存器在模块复位、系统复位甚至上电复位后内容都不会清除。因此,在系统初始化阶段,建议先读取一次UERRADDR0UERRADDR1寄存器,将其内容清零到一个已知状态(读UERRADDR0清零为0,读UERRADDR1清零为0x200),避免残留的随机值被误判为错误地址。

3.3 RXRAM溢出缓冲区地址寄存器:诊断数据流堵塞

RXOVRN_BUF_ADDR寄存器是一个高级诊断工具。在多缓冲模式下,如果序列器试图将新接收的数据写入一个RXRAM位置,而该位置的数据尚未被CPU或DMA读取(即RXEMPTY标志为0),就会发生接收溢出。此时,RXOVRN中断标志会被置位,而RXOVRN_BUF_ADDR寄存器会锁存发生溢出的那个RXRAM缓冲区地址。

这个功能的价值在于定位问题。在复杂的多TG调度应用中,如果因为CPU处理不及时或DMA配置不当导致数据丢失,仅凭一个溢出中断标志很难定位是哪个数据流出的问题。通过读取这个寄存器,你可以精确知道是哪个缓冲区的数据被覆盖了,从而调整该缓冲区的BUFMODE(例如从“跳过”模式改为“挂起直到RXEMPTY”模式),或者优化软件的数据处理流程。

操作注意:读取RXOVRN_BUF_ADDR寄存器会自动清除SPIFLG寄存器中的RXOVRN中断标志。因此,在中断服务程序中,应先读取地址进行记录,然后再清除中断标志位。

4. 高级功能与测试寄存器解析

4.1 I/O回环测试控制寄存器:硬件自检的利器

IOLPBKTSTCR寄存器用于模块的生产测试系统自诊断。它允许开发者在不连接外部物理设备的情况下,验证SPI接口的发送和接收通路是否完好。

  • IOLPBKTSTENA (位[11:8]):测试使能密钥。必须写入0xA来启用整个I/O回环测试模式。
  • LPBKTYPE (位1):选择回环类型。
    • 0:数字回环。数据在模块内部直接从发送移位寄存器反馈到接收移位寄存器,测试内核逻辑。
    • 1:模拟回环。数据通过实际的SPI引脚输出并立即从输入引脚读回,可以测试引脚驱动器和接收器的电路是否正常。
  • 错误注入控制位 (CTRL_BITERR, CTRL_DESYNC等):这些位可以主动诱发特定的通信错误(如位错误、失步错误、奇偶错误、超时错误)。这在开发通信协议的容错处理代码时极其有用,你可以模拟各种故障场景,测试你的错误恢复机制是否健壮。

使用示例(数字回环测试)

// 配置MibSPI进入数字回环测试模式,并诱发一个位错误 #define MIBSPI1_IOLPBKTSTCR (*(volatile uint32_t*)(0xFF0E0000 + 0x134)) // 1. 首先,正常配置MibSPI为主机模式,设置好时钟格式等 // ... // 2. 启用数字回环测试模式 MIBSPI1_IOLPBKTSTCR = (0xA << 8); // 设置IOLPBKTSTENA=0xA, LPBKTYPE=0(数字回环) // 3. (可选)诱发一个位错误,测试错误检测逻辑 MIBSPI1_IOLPBKTSTCR |= (1 << 20); // 设置CTRL_BITERR=1 // 4. 此时,通过MibSPI发送数据,数据会在内部被接收。 // 由于CTRL_BITERR被设置,接收到的数据位会被反转,从而触发RXINT中断或产生错误状态。 // 可以验证错误处理流程。 // 5. 测试完成后,务必退出测试模式 MIBSPI1_IOLPBKTSTCR = 0x0; // 写入非0xA的值以禁用测试模式

警告IOLPBKTSTCR寄存器中的功能仅用于测试。在正常应用代码中,除非进行专门的诊断,否则不应启用这些模式,尤其是错误注入功能,它会破坏正常通信。

4.2 SPI扩展预分频寄存器:连接超低速设备

标准SPI格式寄存器(SPIFMTx)中的预分频器(PRESCALE)位宽可能有限(例如8位),限制了所能产生的最低SPI时钟频率。EXTENDED_PRESCALE1EXTENDED_PRESCALE2寄存器通过扩展预分频值的位宽(例如到11位),使得MibSPI能够产生极低的SPICLK,从而与那些工作时钟非常慢的外设(如某些老式传感器或低功耗设备)通信。

关键机制:扩展预分频寄存器与对应的SPIFMTx寄存器中的PRESCALE字段物理上是同一个寄存器的不同位视图。这意味着:

  1. 如果你只需要标准分频(≤256),直接配置SPIFMTx.PRESCALE即可。
  2. 如果你需要更大的分频(>256),你必须先配置SPIFMTx.PRESCALE然后再配置对应的EXTENDED_PRESCALE寄存器。后者的配置会覆盖前者中的高几位,共同组成一个更大的分频值。
  3. 如果之后你又写入了SPIFMTx.PRESCALE,它会自动将EXTENDED_PRESCALE寄存器中的扩展高位清零,以保证分频值的一致性。

计算公式SPICLK频率 = VCLK / (EXTENDED_PRESCALE值 + 1)

配置顺序示例(为SPIFMT1设置大分频)

#define MIBSPI1_SPIFMT1 (*(volatile uint32_t*)(0xFF0E0000 + 0xZZ)) // SPIFMT1地址 #define MIBSPI1_EXT_PRESCALE1 (*(volatile uint32_t*)(0xFF0E0000 + 0x138)) // 扩展预分频1 // ���标:产生 VCLK/1500 的SPICLK。分频值 = 1500 - 1 = 1499 // 1499 的二进制为 10111011011 (11位) // 低8位: 0xDB (11011011) // 高3位: 0x5 (101) // 步骤1:先写入SPIFMT1的低8位预分频值(虽然会被后续覆盖,但顺序必须遵守) MIBSPI1_SPIFMT1 = (MIBSPI1_SPIFMT1 & ~(0xFF << 8)) | (0xDB << 8); // 设置PRESCALE1低8位为0xDB // 步骤2:再写入扩展预分频寄存器,包含完整的11位值 // EPRESCALE_FMT1 在寄存器[26:16],需要左移16位 uint32_t extended_prescale_value = 1499; // 0x5DB MIBSPI1_EXT_PRESCALE1 = (extended_prescale_value << 16); // 同时,EPRESCALE_FMT0字段在[10:0],如果不用应保持为0或配置其他值

5. 多缓冲RAM的配置与实战心得

理解了控制寄存器,最终都要落到对多缓冲RAM这个“工作区”的配置上。这是MibSPI编程的核心。

5.1 TXRAM缓冲区字段详解与配置策略

每个TXRAM缓冲区都是一个完整的传输描述符。除了之前提到的BUFMODE,其他字段同样关键:

  • CSNR (芯片选择号):这是一个位掩码,而非编号。例如,如果你想激活SPICS[0]SPICS[2],需要写入CSNR = (1 << 0) | (1 << 2) = 0x05。这允许同时选中多个从设备,但需谨慎使用,确保从设备支持。
  • CSHOLD (片选保持):置1时,在一次传输结束后片选信号保持有效,直到下一个具有不同CSNRCSHOLD=0的缓冲区被加载。这对于需要连续向同一设备发送多个数据字而不产生片选间隙的协议非常有用。
  • LOCK:当设置为1时,会锁定当前缓冲区和下一个缓冲区(下一个缓冲区的LOCK位为0),在这两个缓冲区传输完成前,更高优先级的TG也无法打断。这用于保证关键数据序列的原子性。
  • WDEL (使能等待延迟):置1后,在一次传输结束后,会插入一个由对应数据格式寄存器中WDELAY字段定义的延迟周期,然后才进行下一次传输。这对于满足某些从设备的最小片选无效时间要求至关重要。

一个典型的TXRAM缓冲区初始化示例: 假设我们要配置缓冲区0,以连续模式向芯片选择0的设备发送数据,使用数据格式0,并在传输间插入延迟。

typedef volatile struct { uint16_t TXDATA; uint16_t CONTROL; } MibSpiBuffer_t; #define MIBSPI_TXRAM_BASE (0xFF0E0000) // MibSPI1 TXRAM基址 MibSpiBuffer_t* txBuf = (MibSpiBuffer_t*)MIBSPI_TXRAM_BASE; void InitTxBuffer0(void) { uint16_t control_word = 0; // BUFMODE = 4 (连续模式) control_word |= (4 << 13); // 位[15:13], 4 << 13 即 4 << 13,实际需看位域 // 注意:上述移位是概念示意,实际应根据寄存器位域精确计算。 // 假设BUFMODE在控制字高16位的[31:29],而我们的control_word是低16位,这里需要转换。 // 更常见的做法是直接操作32位字。 // 更清晰的32位操作方式: uint32_t buffer_value = 0; buffer_value |= (4 << 29); // BUFMODE[31:29] = 4 (连续模式) buffer_value |= (0 << 28); // CSHOLD[28] = 0 (传输后释放片选) buffer_value |= (0 << 27); // LOCK[27] = 0 buffer_value |= (1 << 26); // WDEL[26] = 1 (使能延迟) buffer_value |= (0 << 24); // DFSEL[25:24] = 0 (选择数据格式0) buffer_value |= (1 << 16); // CSNR[23:16] = 0x01 (选择芯片选择0) // TXDATA[15:0] 默认为0,或后续填入 // 写入TXRAM的缓冲区0(每个缓冲区32位,即4字节) uint32_t* txram_32ptr = (uint32_t*)MIBSPI_TXRAM_BASE; txram_32ptr[0] = buffer_value; // 索引0对应缓冲区0 }

5.2 避免接收溢出的实战技巧

接收溢出是多缓冲模式下一个常见的痛点。资料中提到了SUSPEND until RXEMPTY功能(即BUFMODE设置为6或7),这可以彻底避免溢出,但会牺牲吞吐量,因为序列器会停下来等待CPU读取数据。

更优化的策略是结合使用:

  1. 合理划分TG和缓冲区:将高优先级、数据量小的关键数据放在一个TG中,并使用“挂起”模式保证可靠性。将低优先级、数据量大的流数据放在另一个TG中,使用“跳过”模式,并配合DMA进行批量搬运。
  2. 利用RXOVRN中断和地址寄存器:不要害怕溢出,而是管理它。启用RXOVRN中断,并在中断服务程序中读取RXOVRN_BUF_ADDR。这能让你在发生溢出时立刻知道是哪个数据流出了问题,然后可以采取补救措施,例如记录日志、增加该缓冲区的处理优先级,或者在状态监测中标记该数据源暂时不可靠。
  3. 动态调整BUFMODE:在软件中可以根据系统负载动态调整某些缓冲区的BUFMODE。例如,在系统启动初期,所有缓冲区都用“挂起”模式保证数据完整。进入稳定运行后,对非关键通道切换为“跳过”模式以提升整体吞吐。

6. 常见问题排查与调试心得

在实际项目中使用MibSPI,难免会遇到各种问题。以下是一些典型问题的排查思路:

问题1:DMA传输不启动或传输计数不对。

  • 检查DMACNTLEN寄存器的LARGE_COUNT位设置。如果使用大计数模式,务必确认编程顺序:先写DMAxCOUNT,再使能DMAxCTRL中的传输使能位。
  • 检查:DMA的触发源是否配置正确。MibSPI的TX/RX事件需要正确映射到DMA通道的触发输入。
  • 检查:多缓冲RAM的BUFMODE是否允许传输。如果缓冲区处于“挂起”模式且条件不满足,序列器不会启动传输,自然也不会产生DMA请求。

问题2:奇偶错误频繁发生。

  • 检查UERRCTRLEDEN字段是否已正确写入0xA。写入其他值(包括0)都会禁用检测。
  • 排查:是TXRAM错误还是RXRAM错误?通过UERRSTAT区分。TXRAM错误可能发生在CPU写入或序列器读取时,检查软件写入逻辑或内存总线稳定性。RXRAM错误发生在接收数据写入时,检查SPI线路噪声、电源完整性或时钟抖动。
  • 注意:上电后未清除的地址寄存器残留值。在初始化时读取一次UERRADDR0/1寄存器。

问题3:多缓冲序列执行混乱,不按预期顺序跳转。

  • 检查:各个TG的优先级(TGxCTRL寄存器)和触发条件配置。高优先级TG会抢占低优先级TG。
  • 检查:缓冲区BUFMODE配置。一个配置为“挂起”且条件永不满足的缓冲区会阻塞整个TG。
  • 检查LOCK位的使用。如果一个缓冲区设置了LOCK=1,它会和下一个LOCK=0的缓冲区绑定,期间不能被高优先级TG打断。
  • 调试建议:充分利用MibSPI的调试功能,如当前TG/缓冲区指针寄存器。通过在线调试器实时观察这些指针的变化,可以清晰地看到序列器的执行流。

问题4:SPI时钟频率达不到或计算不准。

  • 检查:是否使用了扩展预分频。如果需要很低频率,确认配置顺序:先SPIFMTx.PRESCALE,后EXTENDED_PRESCALE
  • 计算:使用公式BR = VCLK / (Prescale + 1)。注意Prescale是分频值,写入寄存器的值等于Prescale
  • 验证:用示波器测量实际SPICLK引脚输出,与计算值对比。注意VCLK的频率是否与系统时钟配置一致。

个人踩坑记录:曾经在一个项目中,MibSPI的接收数据偶尔会错位。排查了很久,最后发现是多个TG的缓冲区在RAM中地址范围配置有重叠。虽然每个TG有自己的控制寄存器定义范围,但如果软件计算偏移错误,导致两个TG的缓冲区指向了同一个物理RAM位置,就会发生数据互相覆盖的诡异现象。教训是:在初始化多个TG时,务必仔细计算每个TG的起始缓冲区和缓冲区数量,确保它们在RAM中划分的区间是严格不重叠的。画一张内存映射图在项目初期非常有帮助。