1. 项目概述与核心价值
在嵌入式系统开发,尤其是基于ARM Cortex-M4内核的Tiva™ TM4C1294NCPDT这类高性能微控制器的项目中,我们常常面临两个看似基础却至关重要的挑战:如何让MCU与外部存储或高速外设“对话”得又快又稳,以及如何确保传输过程中的数据“毫发无损”。前者关乎系统性能的上限,后者则决定了系统运行的可靠性下限。很多开发者拿到芯片手册,看到EPI(External Peripheral Interface)和CRC(Cyclical Redundancy Check)模块那几十页的寄存器描述,往往感到无从下手,要么照搬例程不求甚解,要么参数配置不当导致系统不稳定。
我过去在工业通信和车载数据记录仪项目中,就曾因为EPI时序配置不当,导致连接的PSRAM频繁出现数据错位,又因为CRC校验模式选择错误,让整个系统的通信容错机制形同虚设。踩过这些坑之后,我才深刻理解,寄存器配置不是简单的填数字游戏,其背后是信号完整性、时序余量和算法标准的精密权衡。本文将以TM4C1294NCPDT为例,抛开官方手册的碎片化描述,从一线工程师的视角,系统性地拆解EPI主机总线时序的优化逻辑和CRC模块的实战配置要点。无论你是正在调试一块新的核心板,还是试图压榨现有硬件的数据吞吐潜力,这里提供的思路和具体寄存器操作,都能让你少走弯路,直接构建起稳定、高效的嵌入式通信与校验框架。
2. EPI主机总线时序深度解析与配置哲学
EPI模块是TM4C129x系列微控制器的一大亮点,它提供了类似MCU外部总线的并行接口,可以高效连接SRAM、PSRAM、FPGA或CPLD等设备。其核心难点在于时序匹配:MCU内部时钟频率高达120MHz,而外部设备的速度千差万别,如何让两者在时间上“步调一致”,就是EPI时序寄存器配置要解决的根本问题。
2.1 理解EPI时序模型:时钟、等待与就绪
EPI的主机总线模式(Host-Bus Mode)时序主要由几个关键参数决定,它们共同描绘了一次读或写操作的“时间图谱”:
- EPI时钟(EPI Clock):所有时序的基准时间单位。
- 等待状态(Wait States):在地址建立后,数据有效前,EPI控制器主动插入的固定延时周期数,用于匹配低速外设的访问时间。
- 输入就绪延迟(iRDY Delay):当使用外设的“就绪”信号(如iRDY)进行异步握手时,此参数定义了从采样到iRDY有效到EPI开始插入等待的时钟延迟。这是实现与可变延时外设可靠通信的关键。
- 传输间捕获宽度(Capture Width):在一次传输结束和下一次传输开始之间,EPI控制器强制插入的空闲周期,用于满足某些外设(尤其是PSRAM)的行预充电(Precharge)或恢复时间要求。
官方手册给出了多个EPIHB16TIMEn和EPIHB8TIMEn寄存器(n=2,3,4对应片选CS1n, CS2n, CS3n),其结构大同小异。以EPIHB16TIME2(偏移0x314)为例,我们需要关注的几个核心字段及其实际影响如下:
- RDWSM (Bit 0) / WRWSM (Bit 4):读/写“等待状态减一”控制位。这是一个使能位,而非数值位。当它置1时,实际的等待状态数 = 对应配置寄存器(
EPIHB16CFG2中的RDWS或WRWS字段)的值减一。这为你提供了更精细的时序调节能力。例如,若RDWS配置为3个周期,RDWSM=0则实际为3周期,RDWSM=1则实际为2周期。关键点:此位在突发(BURST)模式下不适用,突发传输有独立的时序控制。 - CAPWIDTH (Bits 13:12):传输间捕获宽度。它定义了连续两次访问之间必须间隔的EPI时钟周期数(1或2个周期)。对于需要行预充电的PSRAM,这个参数至关重要。设置过小可能导致预充电时间不足,引发数据错误;设置过大则会无谓降低总线效率。
- IRDYDLY (Bits 25:24):输入就绪延迟。当使用iRDY信号进行流控制时,此字段决定了EPI在采样到iRDY有效(低电平)后,等待多少个EPI时钟周期才开始其内部的等待状态计数。这相当于给外设的响应留出了一个“缓冲时间”。选项有1、2、3个时钟周期。调试经验:如果外设的iRDY信号响应较慢,或者PCB走线较长引入了延迟,适当增加
IRDYDLY可以避免EPI在iRDY稳定前就误判为设备未就绪。 - PSRAMSZ (Bits 18:16):PSRAM行大小。此字段仅在与PSRAM连接且配置为相应模式时有效。它定义了PSRAM的行大小(从128字节到8KB),EPI控制器利用此信息在行边界自动插入预充电周期。务必注意:此值必须与你实际使用的PSRAM芯片规格完全一致,否则跨行访问时必然出错。
2.2 配置流程与实战参数计算
配置EPI时序不是一蹴而就的,而是一个“计算-配置-测试-微调”的迭代过程。下面以一个连接70ns访问时间的16位异步SRAM为例,展示完整的配置思路。
步骤一:确定基础时序参数假设系统EPI时钟配置为60MHz(周期约16.67ns)。SRAM的读取访问时间t_AA为70ns。
- 计算最小所需时钟周期数:70ns / 16.67ns ≈ 4.2个周期。这意味着,从地址有效到数据被稳定读取,至少需要5个EPI时钟周期(因为周期数必须为整数,且要向上取整以保证余量)。
- 分解EPI时序:一个基本的读周期通常包含:地址建立时间(由
EPIHB16CFG2的ASIZE等字段设置)+ 等待状态(RDWS)+ 数据捕获时间。为简化,假设地址建立已设为1个周期。那么,等待状态RDWS至少需要设置为 5 - 1 = 4个周期。 - 应用RDWSM:如果我们希望更精确地控制为4个周期,则设置
RDWS=4,RDWSM=0。如果我们发现4个周期仍有偶发错误,实测需要5个周期,但RDWS最大只能设到4(举例),则可以采用RDWS=5,RDWSM=1的组合,实际效果也是5-1=4个周期。这里的灵活性在于,当配置寄存器的范围不足以直接满足需求时,RDWSM/WRWSM提供了额外的调节粒度。
步骤二:配置寄存器假设我们使用CS1n片选,工作在16位主机总线模式(EPICFG.MODE = 0x3)。我们需要配置EPIHB16CFG2和EPIHB16TIME2。
// 1. 首先配置基础控制寄存器 EPIHB16CFG2 (假设基址 EPI_BASE = 0x400D0000) // 设置读等待状态 RDWS = 4, 写等待状态 WRWS = 2(通常写比读快) HWREG(EPI_BASE + EPI_O_HB16CFG2) = (4 << EPI_HB16CFG2_RDWS_S) | (2 << EPI_HB16CFG2_WRWS_S); // 2. 配置时序扩展寄存器 EPIHB16TIME2 uint32_t ui32Time2Reg = 0; // 设置 PSRAMSZ,若非PSRAM或无限行,设为0 ui32Time2Reg |= (0 << EPI_HB16TIME_PSRAMSZ_S); // 设置 IRDYDLY,若不使用iRDY信号,设为0;若使用且信号质量一般,可设为1或2 ui32Time2Reg |= (1 << EPI_HB16TIME_IRDYDLY_S); // 设置 CAPWIDTH,对于普通SRAM,1个周期通常足够 ui32Time2Reg |= (1 << EPI_HB16TIME_CAPWIDTH_S); // 设置 WRWSM 和 RDWSM,这里我们都不启用“减一”功能 // ui32Time2Reg |= (0 << EPI_HB16TIME_WRWSM_S); // 默认0 // ui32Time2Reg |= (0 << EPI_HB16TIME_RDWSM_S); // 默认0 HWREG(EPI_BASE + EPI_O_HB16TIME2) = ui32Time2Reg;步骤三:关键注意事项与避坑指南
- 模式匹配是前提:
EPIHB16TIMEn寄存器仅在EPICFG.MODE = 0x3(主机总线16位模式)时生效。EPIHB8TIMEn仅在MODE = 0x2(主机总线8位模式)时生效。在配置时序寄存器前,务必先正确设置工作模式,否则配置无效。 - 保留位处理:手册中明确强调“Software should not rely on the value of a reserved bit”。在读写这些寄存器时,必须使用“读-修改-写”操作,保留原有保留位的值。直���赋值
0x00022000这样的复位值可能会在未来型号兼容性上埋雷。
// 正确的“读-修改-写”示例 uint32_t ui32RegValue = HWREG(EPI_BASE + EPI_O_HB16TIME2); ui32RegValue &= ~(EPI_HB16TIME_IRDYDLY_M | EPI_HB16TIME_CAPWIDTH_M); // 清除目标位域 ui32RegValue |= (1 << EPI_HB16TIME_IRDYDLY_S) | (1 << EPI_HB16TIME_CAPWIDTH_S); HWREG(EPI_BASE + EPI_O_HB16TIME2) = ui32RegValue;- PSRAM的特殊性:如果连接PSRAM,
PSRAMSZ必须按芯片手册设置。同时,CAPWIDTH通常需要设置为2或更高,以满足t_RP(行预充电时间)的要求。最好的方法是查阅PSRAM数据手册,找到t_RC(读周期时间)和t_RP,然后用EPI时钟周期去换算。 - 时序验证:配置完成后,最可靠的验证方式是使用逻辑分析仪或示波器抓取EPI总线波形(地址线、数据线、片选、读使能)。实测关键时间参数(如地址有效到数据有效的时间
t_AA)是否满足外设要求并留有适当余量(建议>10%)。软件上可以进行连续地址的读写压力测试,并回读校验。
3. CRC模块:硬件加速的数据守护者
CRC校验是确保数据在传输或存储过程中完整性的基石。TM4C1294的CRC模块将其硬件化、流水线化,将CPU从繁重的循环计算中解放出来,尤其适合处理大数据块或高速数据流。
3.1 四大CRC算法与TCP校验和的选型考量
模块支持四种标准CRC多项式和一个TCP校验和算法,选择哪一个完全取决于你的通信协议或存储格式:
- CRC-16-CCITT (多项式 0x1021):广泛用于X.25、蓝牙HCI、SD/MMC卡命令等。其初始值常为0xFFFF或0x0000,结果异或值为0x0000。
- CRC-16-IBM (多项式 0x8005):也称为CRC-16,用于Modbus、USB数据包等。初始值常为0x0000,结果异或值有时为0x0000,有时为0xFFFF(需查看具体协议)。
- CRC-32-IEEE 802.3 (多项式 0x04C11DB7):以太网帧、ZIP、PNG等文件格式的标配。初始值通常为0xFFFFFFFF,结果异或值为0xFFFFFFFF,且输出反转(Reflect Out)。
- CRC-32C (Castagnoli) (多项式 0x1EDC6F41):在iSCSI、SCTP、EXT4文件系统中流行,因其在硬件实现和错误检测能力上有优势。初始值和异或值常为0xFFFFFFFF。
- TCP/IP Checksum:这是一个16位的一的补码和(One‘s Complement Sum),用于TCP、UDP、IP头部校验。注意,硬件计算的是补码和,最终取反得到校验和是软件的工作。
选型心得:绝对不要凭感觉选择算法。必须严格遵循你所要交互的上层协议规范。例如,与一个已有的工业设备通信,对方使用Modbus RTU协议,你就必须使用CRC-16-IBM(0x8005),并且初始值、输入输出反转等参数都要与Modbus标准一致,否则双方校验结果永远对不上。
3.2 寄存器配置详解与数据馈送技巧
CRC模块的配置集中在CRCCTRL寄存器。理解每个位的含义,是正确使用的关键。
CRCCTRL关键字段解析:
- TYPE (Bits 3:0):算法选择。这是最重要的设置。0x0: CRC-16-IBM, 0x1: CRC-16-CCITT, 0x2: CRC-32-IEEE, 0x3: CRC-32C, 0x8: TCP校验和。
- INIT (Bits 14:13):初始化种子控制。00=使用
CRCSEED寄存器中的值作为种子;10=种子初始化为全0;11=种子初始化为全1。这是一个自清除位,在第一次向CRCDIN写入数据后,该字段会自动清零。这意味着如果你想在计算中途重置CRC并更换种子,需要重新配置INIT字段并再次写入种子值。 - SIZE (Bit 12):数据输入大小。0=32位字,1=8位字节。这个选择影响你喂数据的方式。
- ENDIAN (Bits 5:4):字节序控制。用于调整输入数据的字节顺序。假设内存中一个32位字是
0x12345678(地址低字节存0x78,高字节存0x12,即小端模式),而你希望按0x12, 0x34, 0x56, 0x78的顺序进行CRC计算,就需要配置字节序交换。 - BR (Bit 7):位反转使能。将每个输入字节的比特顺序(Bit7->Bit0, Bit6->Bit1...)反转。某些协议(如CRC-16-CCITT用于SD卡)要求对每个输入字节先进行位反转。
- RESINV (Bit 9):结果取反使能。将最终CRC结果按位取反。很多协议(如CRC-32)要求最终结果取反。
- OBR (Bit 8):输出字节反转。将结果寄存器中的每个字节内的比特顺序反转。注意:
BR是针对输入数据,OBR是针对输出结果,两者可独立使用。
数据馈送(Feeding Data)的两种模式:
- 字节模式(SIZE=1):每次向
CRCDIN寄存器写入一个字节(写入低8位,高24位忽略)。数据流必须严格按照协议顺序写入。例如,要计算字符串“ABCD”的CRC,你需要依次写入‘A’、‘B’、‘C’、‘D’的ASCII码。 - 字模式(SIZE=0):每次向
CRCDIN写入一个32位字。这时要特别注意内存中的数据布局和ENDIAN配置。如果内存中的数据是小端格式,而你希望按大端顺序计算,就需要设置ENDIAN进行字节交换。
一个完整的CRC-32计算流程示例(模拟以太网帧FCS计算):
// 假设数据存放在pData指向的缓冲区,长度为dataLen字节 void CalculateCRC32(const uint8_t *pData, uint32_t dataLen, uint32_t *pCrcResult) { // 1. 使能CRC模块时钟(在系统控制模块中) HWREG(SYSCTL_RCGC0) |= SYSCTL_RCGC0_CRC; // 2. 配置CRCCTRL: CRC-32 IEEE,初始化为全1,输入为字节模式,结果取反 HWREG(CRC_BASE + CRC_O_CTRL) = (0x2 << CRC_CTRL_TYPE_S) | // CRC-32-IEEE (0x3 << CRC_CTRL_INIT_S) | // 初始化为全1 (CRC_CTRL_RESINV) | // 结果取反 (1 << CRC_CTRL_SIZE_S); // 字节模式 // 假设不需要字节序转换和位反转 // 3. 对于CRC-32-IEEE,种子初始化为全1已由INIT字段完成,无需写CRCSEED // 如果INIT=0,则需要:HWREG(CRC_BASE + CRC_O_SEED) = 0xFFFFFFFF; // 4. 馈送数据 for(uint32_t i = 0; i < dataLen; i++) { // 以字节模式写入,只使用最低字节 HWREG(CRC_BASE + CRC_O_DIN) = (uint32_t)pData[i]; } // 5. 读取结果(从后处理结果寄存器读取,因为RESINV使能了) *pCrcResult = HWREG(CRC_BASE + CRC_O_PP_RESULT); }3.3 结合μDMA实现零CPU开销的流式计算
CRC模块与μDMA的配合是其性能发挥的关键。你可以配置一个μDMA通道,将源数据(如从UART接收缓冲区、Flash存储器)自动搬运到CRCDIN寄存器。配置的关键点在于:
- 将μDMA通道的目的地址设置为
CRCDIN的地址。 - 设置目的地址为非递增模式(因为始终写入同一个寄存器)。
- 数据大小(Arbitration Size)根据
CRCCTRL.SIZE设置:字节模式为8位,字模式为32位。 - 在μDMA传输完成中断中,读取
CRCRSLTPP寄存器获取最终校验结果。
这种模式下,CPU仅在初始化时配置CRC和μDMA,在传输完成后处理中断即可,期间可以处理其他任务,极大地提高了系统效率,特别适合高速数据记录或通信协议处理。
4. 典型问题排查与调试实录
即使按照手册配置,在实际调试中依然会遇到各种问题。以下是我在项目中遇到的几个典型案例及其解决方法。
4.1 EPI访问不稳定,偶发数据错误
现象:通过EPI读���外部SRAM的数据,大部分时间正确,但长时间运行或在特定地址段访问时,会偶发读出错误数据。排查思路:
- 检查电源与滤波:首先用示波器检查EPI接口和外部SRAM的电源纹波。高速并行总线对电源噪声非常敏感,确保电源引脚有足够的去耦电容(通常每个电源引脚一个0.1uF MLCC,并配合一个大容量钽电容)。
- 审视时序余量:使用逻辑分析仪捕获出错的读周期波形。重点测量:
t_AA(Address Valid to Data Valid):是否小于SRAM手册要求的最小值?t_OE(Output Enable to Data Valid):EPI的读使能信号宽度是否足够?- 数据建立/保持时间(Data Setup/Hold Time):在EPI采样数据窗口,数据是否稳定?
- 调整等待状态与iRDY:如果测量值接近临界点,首先尝试增加
RDWS或使能RDWSM来增加等待状态。如果使用了iRDY,检查iRDY信号的质量和时序,考虑增加IRDYDLY。 - 检查PCB布局:EPI的高速并行线(尤其是数据线)是否等长?是否远离噪声源(如开关电源、电机驱动)?过长的走线或严重的分支会导致信号反射和时序偏移。必要时,在驱动端串联小电阻(如22Ω)进行阻抗匹配。最终解决:在一个项目中,发现是
CAPWIDTH设置过小。虽然连接的是SRAM,但PCB走线较长,导致信号振铃。将CAPWIDTH从1个周期增加到2个周期,在连续访问间增加了恢复时间,问题得以解决。
4.2 CRC计算结果与标准工具不一致
现象:使用TM4C1294硬件CRC模块计算出的CRC32值,与PC上使用标准库(如Python的binascii.crc32)或在线CRC计算器得到的结果不同。排查清单:
- 核对多项式:确认
CRCCTRL.TYPE选择的是0x2(CRC-32-IEEE 0x04C11DB7),而不是其他多项式。 - 检查初始值:CRC-32-IEEE通常初始值为0xFFFFFFFF。检查
CRCCTRL.INIT是否设置为0x3(全1),或者是否手动向CRCSEED写入了0xFFFFFFFF。 - 确认输入数据:
- 字节序:你的数据在内存中是如何存放的?如果计算一个字符串“123456789”,你是按字节顺序
0x31, 0x32, ... 0x39写入,还是按32位字0x34333231, 0x38373635, 0x00000039写入?这需要与SIZE和ENDIAN设置严格匹配。一个常见错误是忽略了内存的小端存储。例如,一个32位变量0x12345678在内存中(低地址到高地址)存放为0x78, 0x56, 0x34, 0x12。如果你按字模式(SIZE=0)且ENDIAN为默认值(不交换)写入,硬件实际处理的字节顺序是0x78, 0x56, 0x34, 0x12,这可能与协议要求的0x12, 0x34, 0x56, 0x78顺序相反。 - 位反转:协议是否要求对每个输入字节进行位反转(Bit Reflection)?例如,CRC-16-CCITT用于SD/MMC卡命令时就需要。检查
CRCCTRL.BR位。
- 字节序:你的数据在内存中是如何存放的?如果计算一个字符串“123456789”,你是按字节顺序
- 检查输出处理:
- 结果取反:CRC-32-IEEE要求最终结果取反(即与0xFFFFFFFF异或)。检查
CRCCTRL.RESINV是否置位。 - 输出位反转:某些协议要求对最终结果的每个字节进行位反转。检查
CRCCTRL.OBR位。 - 读取的寄存器:你是从
CRCSEED还是CRCRSLTPP读取结果?当使能了RESINV或OBR后,必须从CRCRSLTPP读取经过后处理的结果。CRCSEED里是原始计算结果。
- 结果取反:CRC-32-IEEE要求最终结果取反(即与0xFFFFFFFF异或)。检查
调试技巧:采用“分步验证法”。先计算一个已知标准结果的短数据(例如,单个字节0x31(‘1’) 的CRC-32值)。在PC上用可靠工具算出结果,然后在MCU上,通过调试器单步执行,每执行一步(写配置、写种子、写数据),都读取中间寄存器值进行比对,能快速定位配置在哪一步出现偏差。
4.3 μDMA搬运数据至CRC模块失败
现象:配置了μDMA通道将数据从内存搬运到CRCDIN,但传输完成后CRCSEED寄存器值未更新或错误。排查步骤:
- 检查通道映射与使能:确认在
DMACHMAPn寄存器中,已将正确的通道映射到CRC模块的请求信号。确认μDMA和CRC模块的时钟都已使能(RCGCDMA和RCGCCM)。 - 核对目的地址:μDMA通道控制字中的目的地址必须是
CRCDIN的绝对地址(0x4403_0414),并且目的地址增量(DSTINC)必须设置为0(非递增)。 - 检查数据大小:
CRCCTRL.SIZE设置的数据大小必须与μDMA通道配置的目的数据大小一致。如果CRC设为字节模式(SIZE=1),μDMA的目的传输宽度也必须设为8位。 - 确认传输模式:确保μDMA通道控制字中的循环模式(
CYCLE)和仲裁大小(ARBSIZE)设置正确。对于一次性传输大量数据,通常使用“基本”模式(MODE=0)并设置合适的仲裁大小。 - 查看中断状态:使能μDMA传输完成中断,并在中断服务程序中检查通道状态,确认传输是否真的成功完成,而非因错误停止。根本原因:在一个案例中,问题根源是μDMA的通道优先级冲突。两个高优先级的外设DMA持续占用总线,导致通往CRC模块的DMA请求被长时间阻塞。通过调整通道优先级或优化DMA触发策略解决了问题。
5. 高级应用:PSRAM的优化配置与CRC流式处理框架
5.1 PSRAM的EPI配置实战
PSRAM(Pseudo SRAM)因其高密度和低成本常用于需要较大内存的嵌入式设备。但其访问时序比SRAM复杂,涉及行激活(ACTIVE)、列访问和预充电(PRECHARGE)周期。
配置要点:
- 正确设置
PSRAMSZ:这是EPI控制器能自动管理行边界的关键。假设你的PSRAM行大小是1024字节(1KB),则PSRAMSZ应设置为0x4。当连续访问的地址跨越1KB边界时,EPI会自动插入一个预充电周期和新行激活周期,你无需在软件中干预。 - 合理配置
CAPWIDTH:这个参数直接影响预充电时间(t_RP)。假设PSRAM的t_RP最小为15ns,EPI时钟周期为16.67ns,那么CAPWIDTH至少需要设置为1个周期(16.67ns > 15ns)。为了保险,通常设置为2个周期。 - 使用iRDY信号(如果支持):一些高性能PSRAM提供iRDY或Wait信号。启用此功能(在
EPIHB16CFGn中配置),并设置合适的IRDYDLY,可以让PSRAM自己控制数据就绪时间,实现最有效率的访问。 - 突发模式(Burst Mode)考虑:对于支持突发读写的PSRAM,在
EPIHB16CFGn中启用突发模式可以大幅提升顺序访问的带宽。注意,在突发模式下,RDWSM和WRWSM位不再生效,等待状态由其他突发时序参数控制。
示例配置片段(连接1KB行大小的PSRAM):
// 配置 EPIHB16CFG2 用于 PSRAM uint32_t ui32Cfg2 = 0; ui32Cfg2 |= (3 << EPI_HB16CFG2_RDWS_S); // 读等待状态 ui32Cfg2 |= (1 << EPI_HB16CFG2_WRWS_S); // 写等待状态 ui32Cfg2 |= EPI_HB16CFG2_PSRAM; // 启用PSRAM模式 // ... 其他配置,如地址模式、数据宽度等 HWREG(EPI_BASE + EPI_O_HB16CFG2) = ui32Cfg2; // 配置 EPIHB16TIME2 uint32_t ui32Time2 = 0; ui32Time2 |= (4 << EPI_HB16TIME_PSRAMSZ_S); // 1024字节行大小 (0x4) ui32Time2 |= (2 << EPI_HB16TIME_CAPWIDTH_S); // 2个时钟的传输间隔,满足t_RP // 如果PSRAM有iRDY,则启用并设置延迟 // ui32Time2 |= (1 << EPI_HB16TIME_IRDYDLY_S); HWREG(EPI_BASE + EPI_O_HB16TIME2) = ui32Time2;5.2 构建健壮的CRC流式处理框架
对于需要持续计算CRC的应用(如网络数据包校验、文件系统写入校验),一个结合μDMA和双缓冲(Double Buffer)的框架非常有效。
设计思路:
- 双缓冲区:在内存中开辟两个缓冲区(Buffer A和Buffer B)。
- 流水线操作:
- 阶段1:CPU或外设(如以太网MAC)向Buffer A填充数据。
- 阶段2:启动μDMA,将Buffer A的数据搬运到CRC模块进行计算。同时,CPU/外设可以开始向Buffer B填充下一批数据。
- 阶段3:Buffer A的CRC计算完成,产生中断。在中断中,处理CRC结果(如与预期值比较),然后启动μDMA将Buffer B的数据搬运到CRC模块。同时,CPU/外设回头向已处理完的Buffer A填充新数据。
- 链式μDMA:可以配置两个μDMA通道(主通道和备用通道),使用链式���输(Channel Linking)。当主通道完成Buffer A的传输后,自动加载备用通道的描述符(指向Buffer B),实现无缝切换,进一步减少CPU干预。
代码结构示意:
// 简化框架,展示思路 volatile uint32_t g_ui32BufferReady = 0; // 缓冲区就绪标志 uint8_t g_pui8BufferA[BUFFER_SIZE]; uint8_t g_pui8BufferB[BUFFER_SIZE]; void SetupCRC_DMA_Streaming(void) { // 1. 配置CRC模块(略) // 2. 配置两个μDMA通道描述符 // 描述符A:源地址 = g_pui8BufferA, 目的地址 = CRCDIN, 传输完成链接到描述符B // 描述符B:源地址 = g_pui8BufferB, 目的地址 = CRCDIN, 传输完成链接到描述符A // 3. 启动第一个传输 } // DMA传输完成中断服务程序 void DMA_CRC_Handler(void) { uint32_t ui32Status = // 读取DMA中断状态; if(ui32Status & DMA_COMPLETE_MASK) { // 1. 读取当前的CRC结果 uint32_t ui32CrcResult = HWREG(CRC_BASE + CRC_O_PP_RESULT); // 2. 处理结果(存储、比较等) ProcessCRCResult(ui32CrcResult); // 3. 清除中断标志 // 4. μDMA会自动通过链接触发下一个缓冲区的传输 // 5. 切换缓冲区就绪标志,通知主程序可以填充新数据 g_ui32BufferReady ^= 1; // 切换标志位 } }这种框架将CRC计算完全硬件化、流水线化,CPU仅负责准备数据和处理结果,能够应对极高的数据吞吐率需求,是提升系统整体性能的利器。