ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SPI模式读写SD卡总出错?时序和状态机两大坑必须填平

SPI模式读写SD卡总出错?时序和状态机两大坑必须填平 调了一周的SD卡读写数据还是随机出错。读出来的文件有时候完整有时候最后一个扇区全是0xFF更有时候中间几个字节直接错位。这种“薛定谔的数据”十有八九不是SD卡本身坏了而是SPI模式下时序和状态机这两道门槛没过。如果你正在用SPI总线驱动SD卡或者准备在STM32、ESP32这类主控上挂个SD卡做日志存储、固件升级那这篇文章就是给你写的。我会直接告诉你整个SPI模式下的SD卡读写过程中最容易踩的坑在哪里、为什么会踩进去以及怎么把状态机写得足够稳健让数据再也不“随机失忆”。这篇文章不按芯片手册的章节顺序讲那玩意儿我自己看都费劲。我按排查问题的思路来先搞明白SD卡在SPI模式下到底是什么工作状态然后逐个拆解初始化时序、数据读写状态机、片选管理这几大出错源头最后给一个可以直接往工程里搬的状态机模板。全程用踩坑经验说话字面意义上的实操干货。1. 先把SD卡的SPI工作模式搞明白一块SD卡如何被“降级”使用1.1 SD卡本来不是SPI设备很多人以为SD卡天生就是SPI接口其实这是个误会。SD卡原生的通信方式是SD总线协议命令线是双向的数据线有四根DAT0-DAT3时钟独立整个协议栈比SPI复杂得多。SPI模式是SD卡为了兼容低端控制器而保留的一种“妥协模式”。所谓妥协指的是它牺牲了性能换来了通用性。在SPI模式下SD卡的物理接口被映射成这样原来的一条双向命令线变成了两个方向分离的信号MOSI发命令、MISO收响应四根数据线里只用一根DAT0来传数据DAT1-DAT3被忽略。这正好和STM32、ESP32这些主控的SPI外设对上。但这不代表SD卡就变成了一个普通的SPI从机。它依然保留着SD命令集的操作逻辑仍然需要按它的命令-响应流程办事只是传输协议细节被替换成了SPI的字节流形式。也就是说你表面上在用SPI外设“发送字节”实际上是在操控一个2字节级、命令驱动的状态机。1.2 进入SPI模式的唯一钥匙CMD0加CS低电平要把SD卡从SD模式切换到SPI模式不是简单把四根线一接就行。SD卡在上电默认处于SD模式需要你在CS引脚保持高电平的状态下送出至少74个SPI时钟周期业界习惯多送一点我一般送80个然后在CS拉低之后立即发送CMD0。CMD0是复位命令发送时参数为全零CRC字节必须固定为0x95。这里有个很多人忽略的细节在进入SPI模式之前卡内的CRC校验是开启的所以CMD0的CRC必须正确否则卡直接无视你。等卡回复R1响应且byte为0x01表示进入IDLE状态才算真正进入SPI模式。之后卡内的CRC校验自动关闭后面所有命令的CRC字节就都可以填0x00了。1.3 SPI模式下SD卡的“三个世界”成功进入SPI模式之后和SD卡通信就可以拆成三个层次去理解命令层发送6字节命令帧1字节命令号4字节参数1字节CRC等待卡返回若干字节的响应。数据层读操作时等待数据起始令牌0xFE然后连续读512字节写操作时先发送0xFE再连续发512字节加2字节CRC。状态层卡内部在做擦写、初始化时会通过MISO线输出低电平表示“忙”输出高电平表示“空闲”。绝大多数数据出错恰恰是在这三个层次之间的切换细节上出的问题而不是在某个具体字节的计算上。所以接下来的内容全部围绕这三个层次展开。2. 数据出错的头号元凶初始化时序和时序参数的坑2.1 上电延迟和74个时钟周期到底卡在谁的头上SD卡对初始化时序的要求非常死板。先看上电SD卡内部有电荷泵和电压检测电路需要时间稳定。手册规定上电后至少等待1ms再开始任何操作实际工程里我通常延时几百毫秒反正主控上电慢也不差这点时间。重点在于那74个SPI时钟周期。它们必须在CS为高电平时发送作用是让SD卡内部的时钟同步电路锁存SCK的极性和频率。如果你一上电就直接拉低CS发CMD0卡很可能不响应或者响应乱码。关于这74个时钟我再补一句。我看到有人直接用SPI外设连续发送0xFF字节来产生时钟这是对的但要确保SPI外设的频率配置已经完成且SCK线确实在翻转。有些主控如果SPI没使能就调用收发函数SCK压根不动卡自然“装死”。检查方法也很简单用逻辑分析仪抓一下CS拉低之前的SCK波形确认有脉冲再往后走。2.2 CPOL/CPHA为什么必须老老实实用Mode 0SPI通信里的极性和相位参数是数据出错的第一大隐藏杀手。SD卡的SPI模式文档里明确写着支持SPI Mode 0和Mode 3理论上两种模式都能跑。但实际工程里我强烈建议只用Mode 0。Mode 0的含义是空闲时SCK为低电平数据在SCK上升沿被采样在下降沿变化。SD卡内部逻辑的采样窗口设计绝大多数是基于Mode 0做验证的你用Mode 3即使初始化成功也可能在高速读写时出现偶发性的bit错误而且这种错误极难复现排查起来会让人崩溃。另外还要关注SPI外设的位序设置。SD卡SPI模式要求MSB先发如果你的主控SPI外设默认是LSB first那发出命令帧的命令号字节时位序就是反的。这个错误的表现非常迷惑人响应可能正常也可能乱码取决于命令字节和参数字节是否碰巧形成了合法内容。2.3 时钟频率的选择不是越快越好在SPI模式下SD卡初始化阶段的时钟频率硬件上有硬性上限标准是400kHz。这不是随意给的参数而是因为SD卡内部供电网络和逻辑电路在未完全初始化时无法在更高时钟频率下稳定工作。你如果图省事直接用高频时钟初始化可能出现CMD0响应正常但ACMD41永远卡在0x01无法就绪的情况。等初始化完成之后ACMD41返回0x00可以切换时钟分频系数把频率提上去。标准版SD卡最高支持25MHz高速版支持50MHz。但实际建议取32分频或更低因为还要留出信号完整性和线材质量的余量。特别是在面包板或者杜邦线连接的环境下超过10MHz的SPI时钟很容易因为振铃和串扰产生随机错误。我自己的习惯是初始化阶段100kHz传输阶段10MHz又稳又够用。SD卡读写的瓶颈往往不在总线速度而在卡的随机读写延迟上一味拉高SPI频率收益不大。2.4 片选信号的时序配合片选CS在SPI模式下不仅是“选中设备”那么简单它还参与SD卡的事务边界定义。每一笔完整的读或写操作包括命令、响应、数据、忙等待都应该在一个CS低电平周期内完成。CS一旦拉高SD卡会认为当前事务被强制终止内部状态机直接复位到命令接收状态。这个特性在状态机设计里至关重要。在初始化阶段每一次发送CMD0、CMD8、ACMD41前后都要精确控制CS电平CS拉低发命令等待响应接收完成后CS拉高。CS拉高后还要补至少8个SCK时钟周期让卡完成内部状态的切换。如果省掉这8个时钟卡在下次CS拉低时可能还没准备好返回错误的第一字节。这个问题在MCU主频较高、SPI时钟较快时会频繁出现。初始化完整顺序可以参考这个表格步骤动作说明1初始化SPI引脚CS输出高防止上电时序紊乱误触发卡操作2延时至少10ms等待卡内部电压稳定3CS高电平下发送80个0xFF产生SCK脉冲让卡识别SPI模式4CS拉低发送CMD0CRC0x95复位卡等待R1响应0x015发送CMD8参数0x000001AACRC0x87检查卡支持电压范围和版本6循环发送CMD55ACMD41直到ACMD41返回0x00表示初始化完成7发送CMD58读取OCR判断卡容量类型SDSC/SDHC/SDXC8切换时钟到高速通常10MHz然后进行后续读写初始化阶段哪一步卡住就从上一步开始排查不要跳过步骤去检查后面的读写代码。3. 状态机设计不当导致的“隐蔽性”数据损坏3.1 命令-响应-数据三段式的核心逻辑SD卡的每次读写在软件层面都对应一段清晰的状态流转。我倾向于把它抽象成三段式结构命令阶段、响应阶段、数据阶段。任何一个阶段处理错了数据都会出错但出错的表现完全不同。比如命令阶段发错字节卡要么不响应、要么响应错误码响应阶段接收时序不对命令本身可能已经执行了但你没收到正确响应导致后面数据相位错位数据阶段最坑数据已经在线上传输了你采样时机差一个周期读出来的就是错位的数据。SD卡读单块CMD17的完整状态流转是 发送CMD17 → 等待R1响应 → 持续读字节直到遇到0xFE数据起始令牌 → 连续读512字节数据 2字节CRC → CS拉高结束事务。写单块CMD24则略有不同 发送CMD24 → 等待R1响应 → 发送0xFE数据令牌 → 连续发送512字节 2字节CRC → 读取数据响应字节0x05表示成功 → 循环读取直到MISO变高表示忙结束 → CS拉高结束事务。3.2 状态机的三个典型错误我全踩过先说第一个错命令阶段和响应阶段之间不送时钟。SPI是主从同步通信SCK是由主控控制的。SD卡在接收到命令后需要若干个SCK周期才能准备好响应字节。如果主控发出命令帧后立刻读寄存器读到的往往是0xFF即卡还没把响应放到MISO上。正确做法是发送命令帧后继续发送若干个0xFF字节每发一个0xFF就读一个接收字节直到读到第一个非0xFF字节——这就是R1响应的起始字节。第二个错读数据时把CRC字节当数据。SPI模式虽然禁用了CRC校验但SD卡在数据块后面依然会发送2字节CRC位流这2个字节是必须被主控消费掉的。有些人读完了512字节就直接拉高CS结束事务结果这2字节CRC还没被读走SD卡状态机停留在数据发送状态。下次CS拉低发新命令时卡先吐出来的是上次残留的CRC字节整个通信就错位了。这个问题表现得很隐蔽第一次读写正常第二次开始乱套。解决方式就是老老实实读满5122字节再拉高CS。第三个错写数据后的忙等待不完整。SD卡在完成一次写入后内部需要进行真正的Flash擦写操作这段时间可能长达几十毫秒甚至上百毫秒。在此期间如果主控直接拉高CS发起下一个操作写操作会失败或者数据只写了一部分。正确做法是写数据后持续发送0xFF并读取MISO只要MISO为低电平就说明卡还在忙只有当MISO回到高电平才表示写操作彻底完成。3.3 超时处理是状态机的逃生门任何状态机都必须有超时机制SD卡通信尤其如此。卡在初始化过程中、擦写过程中都可能因为质量问题或者电压波动陷入无限等待。如果状态机没有超时退出机制主控就会被一个卡死的SD卡拖死整个系统都跟着瘫痪。超时参数需要分场景设计命令响应超时从发送命令到最后一位CRC字节的6字节结束后开始计时一般需要100ms。正常情况下R1响应在1-8个字节时间内就会到达超过100ms基本可以认为卡挂了。数据令牌超时读操作中从R1响应结束到0xFE数据令牌出现最多等待100ms。写忙超时从收到数据响应字节0x05到MISO重新变高建议等待500ms。SD卡手册上写的最大擦写时间是200ms左右取500ms留够余量。一旦超时正确动作是先把CS拉高、再连续发送至少8个0xFF清除卡内残留状态然后整体重置SD卡初始化流程。千万不要在超时后直接重试同一个命令那大概率还是死循环。3.4 状态机实现上的一个小建议用枚举不要用裸标志位在实际编码中我建议把所有状态定义为一个枚举类型而不是用几个分散的bool变量去拼凑状态。原因很简单传递函数参数的复杂度低调试时打日志能一眼看出当前状态而且强制switch-case枚举可以避免出现“所有条件都不满足”的死区。实测下来这种设计在后续维护中省了大力气。状态机模板我放到后面单独章节讲。4. 硬件片选与软件片选之争一个电平翻转引发的数据错乱4.1 硬件片选和软件片选的本质区别SPI外设通常带一个硬件片选引脚NSS它可以配置为硬件自动模式主控启动SPI传输时外设自动把NSS拉低传输结束自动拉高。听起来很方便但在SD卡这种“长事务”通信场景下这个自动拉高反而是致命的。读一个扇区512字节中间可能要读五六百个字节包含令牌和CRC。如果主控的SPI TX FIFO深度不够在传输过程中出现短暂的FIFO空导致SCK停顿硬件NSS就会自动拉高。SD卡一检测到CS拉高就认为事务被中断立刻丢弃当前数据块。后续数据全部错位。这种情况在嵌入式实时操作系统中尤其常见高优先级任务抢占了CPU导致SPI传输延迟硬件NSS瞬间抖动。4.2 为什么软件片选更可靠软件片选用普通GPIO去模拟NSS由你的代码在精准的时刻拉低和拉高。它的优点有两个 第一传输过程中CS电平不受SPI外设FIFO状态影响只要程序不在中途手动改GPIOCS就会稳定拉低到整个事务结束。 第二可以精确地在最后一个字节传输完成之后再拉高CS。硬件NSS无法做到“读完全部字节再拉高”这种细致粒度软件GPIO可以。我一直说服团队统一使用软件片选尤其是SD卡这种存储类设备。唯一的代价是初始化时需要多配置一个GPIO为输出多几行代码而已收益却是整个数据链路的稳定性。4.3 片选切换时的细节别急着拉高CS软件片选下有一个细节很容易被忽略在拉高CS之前要确保SCK上已经发送完最后一个时钟沿SPI外设处于空闲状态。如果CS和SCK同时跳变SD卡内部逻辑可能把CS的上升沿当成数据传输中的毛刺导致状态混乱。稳妥做法是先停止SPI传输等待TXE/BSY标志位然后拉高CS再发8个0xFF时钟。这样CS的上升沿发生在SCK空闲的低电平期间绝对干净。用逻辑分析仪抓波形时也会发现正确的片选时序边沿非常锐利没有毛刺。4.4 切换到其他SPI设备时的注意事项很多电路板上SD卡和别的SPI设备比如Flash、显示屏共用一个SPI总线。每次切换设备时除了切换片选还要确保前一个设备的事务完全终止。我遇到过的典型故障是先操作了Flash再操作SD卡第一笔SD卡命令总是不响应。原因就是Flash的CS拉高后没有补时钟周期SPI总线上残留了Flash输出的数据污染了SD卡的输入。这个问题我认为不用太纠结因为解决起来简单每次切换设备前先发送至少16个0xFF字节并在最后一个字节期间切换片选让总线上所有设备都恢复空闲。5. 实战排查思路从“读出错误数据”到根因定位的完整链路5.1 第一步区分是命令阶段出错还是数据阶段出错拿到一个“数据错误”的bug不要急着看数据缓冲区的内容。先看之前卡有没有正确响应命令。我的做法是在关键节点CMD0后、ACMD41循环、CMD17后打串口日志打印收到的响应字节。如果响应字节符合预期说明命令阶段没有问题接下来专心查数据阶段如果响应就不对说明问题出在命令发送或者初始化时序上。这一点极其重要。我见过太多人拿着逻辑分析仪直接去抓数据阶段波形追了半天发现是最开始的初始化时序就没过。先分清是命令层还是数据层排查范围瞬间缩小一半。5.2 第二步分析数据错误的具体形式数据错误的形态往往直接指向根因读出数据全部是0xFF大概率是卡还未准备好就开始了读操作或者0xFE数据令牌没有出现程序把填充字节当成了数据。读出数据全部是0x00大概率是卡忙状态被误判MISO一直被拉低拉低了。第一个字节正确后面全部错位大概率是采样时钟沿配置问题SCK快了一个节拍。随机某个字节出错其他都正常大概率是信号完整性问题线太长或者电源纹波大。写进去再读出来不一致大概率是写数据时的CRC字节没发送或者忙等待不充分。这个映射关系不是绝对精确但作为起点非常有效。配合逻辑分析仪观察实际数据基本能锁定方向。5.3 第三步用逻辑分析仪验证时序逻辑分析仪是排查通信问题的神器必需要有。抓住这几个关键波形验证初始化阶段SCK是否有脉冲、频率是否正确。CS是否在正确时刻拉低拉高是否有中途抖动。发送命令帧时MOSI上的数据是否和预期命令字节一致。响应期间MISO上是否出现了非0xFF的字节。数据阶段MISO上是否出现了0xFE令牌之后是否紧跟着5122字节数据。如果逻辑分析仪显示一切正常但代码层面还是出错那就该怀疑代码本身的逻辑问题了比如你的SPI接收函数读的是RX寄存器还是DR寄存器或者DMA配置的缓冲区长度不对。这些属于纯软件bug逻辑分析仪帮不上忙。5.4 常见错误现象与根因对照表现象可能原因排查方向CMD0无响应上电延迟不够/74个时钟周期缺失/CS时序错误抓初始化波形检查上电时序ACMD41一直返回0x01初始化时钟太高/ACMD41前未先发CMD55降低初始化频率检查命令发送顺序读数据全是0xFF未等待0xFE令牌/CS被提前拉高检查读状态机正确等待0xFE读数据全部错位采样沿配置错误/位序错误确认CPOL/CPHA为Mode0MSB first偶发随机错误信号完整性/电源问题/时钟太高降低SPI时钟缩短线长加滤波电容写数据正常但读出不对写忙等待不充分/CRC字节未消费检查写状态机忙检测逻辑第二次操作失败上次事务未正确结束/CRC残留确保数据阶段读满CRC字节再拉高CS6. 把正确性“焊死”在状态机里一个可复用的SPI读写SD卡状态机模板6.1 状态定义有了前面几节的铺垫这里直接给出一个我自己项目里验证过多次的状态机框架。它不依赖特定主控切到STM32、ESP32或者别的平台只需要替换底层SPI收发函数。核心状态IDLE空闲状态等待读写请求。SEND_CMD打包并发送6字节命令帧。WAIT_RESP连续发0xFF读取响应字节直到非0xFF或超时。WAIT_TOKEN读操作中等待0xFE令牌。READ_DATA连续读取5122字节。WRITE_TOKEN写操作中发送0xFE令牌。WRITE_DATA连续发送5122字节。WAIT_BUSY写操作后等待MISO变高。ERROR错误状态拉高CS补时钟返回错误码。这些状态中WAIT_RESP、WAIT_TOKEN、WAIT_BUSY都需要超时保护。对应地给三个超时计数器数值分别取100ms、100ms、500ms。6.2 关键代码骨架typedef enum { IDLE, SEND_CMD, WAIT_RESP, WAIT_TOKEN, READ_DATA, WRITE_TOKEN, WRITE_DATA, WAIT_BUSY, ERROR } sd_spi_state_t; static sd_spi_state_t state IDLE; static uint8_t cmd_buffer[6]; static uint8_t resp_byte; static uint32_t data_index; static uint32_t timeout_counter; uint8_t sd_spi_send_cmd(uint8_t cmd, uint32_t arg, uint8_t crc) { cmd_buffer[0] 0x40 | cmd; cmd_buffer[1] (arg 24) 0xFF; cmd_buffer[2] (arg 16) 0xFF; cmd_buffer[3] (arg 8) 0xFF; cmd_buffer[4] arg 0xFF; cmd_buffer[5] crc; CS_LOW(); for (int i 0; i 6; i) { spi_write_read(cmd_buffer[i]); } timeout_counter 0; while (timeout_counter 100) { uint8_t byte spi_write_read(0xFF); if (byte ! 0xFF) { resp_byte byte; CS_HIGH(); return byte; } timeout_counter; delay_ms(1); } CS_HIGH(); return 0xFF; // timeout }注意我在这里让CS先拉高。实际使用中拉高CS的时机要按事务边界统一管理不同操作有细微差别。比如写操作中发送完CMD24之后不能立刻把CS拉高因为还要等数据阶段完成。这也是我不建议把命令发送函数和事务控制完全割裂的原因。6.3 读单块的完整状态机示例uint8_t sd_read_block(uint32_t sector, uint8_t *buffer) { if (state ! IDLE) return SD_ERR_BUSY; state SEND_CMD; sd_spi_send_cmd(CMD17, sector 9, 0x00); // sector转为字节地址 state WAIT_TOKEN; timeout_counter 0; while (timeout_counter 100) { uint8_t byte spi_write_read(0xFF); if (byte 0xFE) { break; } timeout_counter; delay_ms(1); } if (timeout_counter 100) { state ERROR; return SD_ERR_TIMEOUT; } state READ_DATA; for (data_index 0; data_index 512; data_index) { buffer[data_index] spi_write_read(0xFF); } spi_write_read(0xFF); // CRC high byte spi_write_read(0xFF); // CRC low byte state IDLE; return SD_OK; }这段代码故意写得简单直接方便你理解状态流转。真实工程里应该在每两个状态之间加日志输出一旦出错能立刻定位出是哪个环节。6.4 写单块的完整状态机示例uint8_t sd_write_block(uint32_t sector, const uint8_t *buffer) { if (state ! IDLE) return SD_ERR_BUSY; state SEND_CMD; sd_spi_send_cmd(CMD24, sector 9, 0x00); state WRITE_TOKEN; spi_write_read(0xFE); state WRITE_DATA; for (data_index 0; data_index 512; data_index) { spi_write_read(buffer[data_index]); } spi_write_read(0x00); // CRC high byte spi_write_read(0x00); // CRC low byte // 读取数据响应字节 uint8_t data_resp spi_write_read(0xFF); if ((data_resp 0x1F) ! 0x05) { state ERROR; return SD_ERR_WRITE; } // 等待忙释放 state WAIT_BUSY; timeout_counter 0; while (timeout_counter 500) { if (spi_write_read(0xFF) 0xFF) { break; } timeout_counter; delay_ms(1); } if (timeout_counter 500) { state ERROR; return SD_ERR_TIMEOUT; } state IDLE; return SD_OK; }写操作比读操作多了一件事读数据响应字节。这个字节的低5位是状态码0x05表示数据已被卡正确接收0x0B表示CRC错误0x0D表示写错误。如果你的数据响应不是0x05就别继续傻等了直接报错并重新初始化卡。6.5 状态机设计里最容易忽略的两个细节第一不要在SPI中断中执行SD卡状态机。SD卡状态机里充满了毫秒级的延时和超时等待放中断里会阻断整个系统的中断响应。正确做法是在RTOS的任务里或者裸机主循环里轮询状态机。第二超时后的恢复动作要统一。我见过有人每个错误分支里都写一段恢复代码结果改了一处忘了另一处出现“超时后卡死”的bug。建议的做法是统一收敛到ERROR状态拉高CS、补8个时钟、重置SD卡初始化流程。别在局部恢复全局统一处理。如果你使用的是FATFS这样的文件系统底层SD卡驱动在初始化阶段为了保证卡的时序安全通常会有一个底层硬件初始化函数确保SPI外设配置正确、GPIO口模式正确、以及时钟开关处于正确状态。很多人在这个函数里漏掉了SPI外设的时钟使能导致卡完全无响应。这类问题排查起来特别费时因为表面看是SD卡的问题实际上连硬件外设都还没正常工作。7. 进阶补充不同主控平台下的移植注意点前面讲的是普适方法论落地到不同平台还是有差异的。简单说几个我实测过的平台适配要点。STM32上使用硬件SPI时要注意SPI的BR波特率预分频寄存器在传输过程中不可修改。如果你打算初始化时用低速、初始化后用高速必须在两个阶段之间关SPI、改BR、重新使能SPI。用HAL库的朋友可以调用HAL_SPI_DeInit和HAL_SPI_Init重新初始化。ESP32平台的SPI是主从一体的以文件ESP-IDF里的spi_master驱动为例SD卡建议使用SPI2_HOST因为它的DMA通道在读写大数据块时表现更稳定。另外ESP32软件模拟SPI时要注意GPIO的驱动能力设置SD卡线材较长时需要把GPIO输出驱动能力调高否则波形边沿变缓会导致错误。FPGA上实现SPI读SD卡我的建议是用三段式状态机第一段做状态转移第二段做组合逻辑判断第三段做时序输出。这样可以避免出现比较严重的竞争冒险问题。尤其是SCK产生那一块不要用计数器直接产生数字脉冲而是用时钟使能信号去门控SCK输出否则SCK的占空比不稳定SD卡会随机出错。对于这些平台如果你只是想要一个能跑通的底层驱动百度能找到一堆例程但一定要亲手验证时序参数是否和本文章里说的一致。很多例程在PC上仿真没问题一旦烧到真机就各种问题就是因为开发者没理解时序细节只是把别人的代码抄了过来。最后再多说一句我在做SD卡项目时被卡得最久的一次原因是SPI外设的FIFO缓冲深度比较小在连续读取512字节数据时DMA配置错误导致数据重复写入缓冲区。回头排查时发现硬件时序完全正常问题出在我对DMA的理解有误。这个经验告诉我遇到数据错误时不要只盯着时序和状态机主控内部的数据通路同样值得怀疑。但是无论如何先把时序和状态机这两道门槛迈过去后续的排查才会有扎实的根基。
返回列表