STM32G031与MT6835 SPI通信调试实录:从0xFF到稳定通信的排查过程

发布时间:2026/9/6 11:32:56
STM32G031与MT6835 SPI通信调试实录:从0xFF到稳定通信的排查过程 一次“见鬼”的调试STM32G031与MT6835的SPI首次通信之谜说实话干嵌入式这么多年SPI通信我写过不下几百次但上个月被一块小小的MT6835折腾到怀疑人生。现象极其简单STM32G031做主MT6835做从标准的SPI主从连接代码是照着数据手册和原厂参考例程抄的逻辑分析仪也挂了可无论我怎么调整时钟极性、相位、分频系数回读的数据就是不对——要么全是0xFF要么偶尔蹦出几个看似正常但完全不符合寄存器手册的“乱码”。这个项目本身并不复杂我们在一块定制板上用了MT6835这颗电机驱动SoC需要上位机STM32G031通过SPI接口去读写它的内部寄存器做参数配置和实时状态监控。MT6835的SPI从机接口支持标准4线模式理论上就是SCLK、MOSI、MISO、CS四根线的事加上G031主频64MHz跑个10MHz以下的SPI时钟绰绰有余。可偏偏这样“毫无难度”的需求让我花了两天半才真正跑通。这篇文章想完整还原这段“见鬼”的排查过程把我在MT6835上踩到的坑、定位根因的方法论、以及最终验证通过的配置写清楚。如果你手里刚好也有一颗MT6835或者你正在做同类带片上Flash的SPI从机芯片通信这篇文章的参考价值会非常大。1. 项目背景与第一次联调的离谱现场先说清楚我们这边的工作环境免得后续排查思路看得云里雾里。1.1 硬件连接与软件栈主控用的是STM32G031G8U6Cortex-M0内核64MHz主频带2个SPI外设其中一个复用为SPI1。MT6835是一颗集成了电机控制逻辑和驱动预驱的SoC通过SPI从机接口开放了内部寄存器访问能力包括配置寄存器、状态寄存器、故障寄存器等地址范围主要集中在0x00到0xFF之间另外还有一段扩展寄存器区。硬件连接PB3 - SCLKSPI1_SCKPB4 - MOSISPI1_MOSIPB5 - MISOSPI1_MISOPB0 - CS软件控制GPIO输出电源方面MT6835的数字IO供电是3.3V和G031一致不需要电平转换。板子是自己画的走线不长SPI信号线都在3cm以内没有串阻也没接上拉SPI是推挽输出一般不需要上拉。软件上用的STM32CubeMX生成初始化代码选择了SPI Mode 0CPOL0, CPHA0然后手写了一个简单的寄存器读写函数uint8_t mt6835_read_reg(uint8_t reg_addr) { uint8_t tx_buf[2] { MT6835_READ_CMD, reg_addr }; uint8_t rx_buf[2] { 0, 0 }; HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); HAL_SPI_TransmitReceive(hspi1, tx_buf, rx_buf, 2, 100); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); return rx_buf[1]; }MT6835手册里写的读操作格式是第一个字节为命令字0x06表示读第二个字节为寄存器地址之后从机在第三个字节开始在MISO上返回数据。上面这段代码是初版只发了两字节就拉高了CS也没等第三个字节。1.2 第一次实测返回数据全是0xFF满怀信心地把固件烧进去打开串口调试助手发了一条读取设备ID寄存器0x00的命令结果返回的是一串0xFF。重复几次稳定复现。复位后再读还是0xFF。这一步我其实没有太慌——SPI第一次联调出现0xFF最常见的原因无非就是MISO没接好、从机没上电、或者CS时序不对。但当我拿逻辑分析仪去抓波形时SCLK、MOSI、CS都有正常的电平翻转MISO也并不是浮空——它一直保持在高电平也就是在从机端MISO被拉死了。高电平的MISO意味着从机要么没在驱动这条线要么就是驱动了但发送的数据位全是1。如果是没驱动加上外部有上拉也会表现为高电平。但我们的板子上MISO没有接上拉那问题就变成了MT6835的MISO引脚内部是不是有上拉还是说它在SPI时序里根本没有正确响应主机1.3 一个很容易被忽略的细节MT6835的IO初始状态MT6835这颗芯片比较特殊的地方在于它的SPI接口引脚是复用的同时承担了芯片启动模式选择和程序烧录功能。手册里明确写了芯片上电时会采样SPI_CS、SPI_CLK两个引脚的电平用于确定启动模式从SPI启动、从内部Flash启动等。这就带来一个非常关键的实际问题主控STM32G031上电时如果它的GPIO默认状态不对在MT6835的采样窗口期内把SPI_CS或者SPI_CLK拉到了不该有的电平就可能让MT6835进入一个非预期的启动分支。回头查G031的GPIO默认状态——STM32全系列在复位后GPIO都是浮空输入模式除了调试引脚。浮空意味着引脚电平由外部电路决定而我们的板子上CS和SCLK都没有外部上下拉这两根线在系统上电瞬间就处于高阻态电平完全不确定。MT6835如果采样到一个不确定的启动电平轻则进入错误启动模式重则直接挂死在某个不该停的状态里——这就能解释为什么MISO会莫名奇妙的拉高。但这个怀疑在当时还只是猜测我需要更直接的证据来验证。2. 排查过程中的那些“几乎成功”的假象2.1 换了软件模拟SPI看似解决了其实是自欺欺人既然怀疑硬件SPI的时序或GPIO复用配置有问题我第一反应是先用GPIO模拟SPI来绕开硬件外设的所有不确定因素。这是我调试SPI设备时很常用的“降维打击”手段——硬件SPI出问题先用软件模拟验证从机是否正常能快速切分问题边界。软件模拟SPI的代码不复杂关键是严格按MT6835手册中的时序图来写void sw_spi_delay(void) { for (volatile int i 0; i 10; i); } uint8_t sw_spi_transfer_byte(uint8_t byte) { uint8_t rx 0; for (int i 7; i 0; i--) { // CPOL0, CPHA0: 空闲时SCLK为低第一个边沿采样 // 发送位: MOSI在SCLK上升沿之前准备好 if (byte (1 i)) { GPIO_SetBits(MOSI_PORT, MOSI_PIN); } else { GPIO_ResetBits(MOSI_PORT, MOSI_PIN); } sw_spi_delay(); // SCLK上升沿主机发送数据、从机采样 GPIO_SetBits(SCLK_PORT, SCLK_PIN); sw_spi_delay(); // SCLK下降沿从机输出数据主机采样MISO rx (rx 1) | GPIO_ReadInputDataBit(MISO_PORT, MISO_PIN); GPIO_ResetBits(SCLK_PORT, SCLK_PIN); sw_spi_delay(); } return rx; }这个代码本身没有逻辑问题CPOL0/CPHA0模式下上升沿移出主机数据、下降沿采样从机数据这是标准做法。换上软件模拟SPI之后我重新读了一遍设备ID寄存器。结果出来了0x00, 0xA5, 0x5A, 0x00中间出现了看似有意义的值——0xA5和0x5A那一刻我内心一阵狂喜觉得问题肯定出在硬件SPI的配置上软件模拟都通了接下来只要回去把硬件SPI参数调对就行。但冷静下来之后再读每次读出来的序列都不一样有时是0x5A, 0x00, 0xFF, 0xA5有时是0xA5, 0x5A, 0x5A, 0x00。这根本不是稳定的寄存器值而是MISO上随机变化的电平组合。2.2 从“寄存器回读值”到“波形本身”的视角切换这时候我突然意识到一个问题我一直在用“读回来的数对不对”来判断通信是否成功但MT6835的MISO上可能同时存在多个信号源。这颗芯片的SPI从机接口背后可能还连着一个片上调试接口或者Flash控制器的引脚如果CS或者SCLK上的毛刺触发了某些内部状态机的误动作MISO上的电平就可能被其他模块驱动表现出“一直在跳变”的假象。与其去猜不如直接看波形。我把逻辑分析仪接到SCLK、MOSI、MISO、CS四根线上抓了一次完整的软件SPI读写过程。波形倒出来之后我看清了几个关键事实SCLK确实有8个完整的脉冲频率大约500kHz符合我软件延时产生的频率。MOSI上发的数据也正确第一字节0x06读命令第二字节0x00寄存器地址。CS在传输期间保持低电平结束后拉高。CS的低电平持续了约200us。但是MISO在整个CS有效期间除了中间出现过几个极窄的毛刺之外大部分时间都是高电平。MISO上的毛刺是问题关键。这些毛刺非常窄大约只有几十纳秒远远小于SCLK的半个周期1us。这说明MISO不是由一个正常的SPI移位寄存器驱动的否则它只会在SCLK的下降沿附近变化呈现出与SCLK同步的、宽度为微秒级的电平跳变。几十纳秒的毛刺更像是某个内部信号线之间的串扰或者是芯片内部某个状态机在切换时的瞬态输出。2.3 时钟极性问题我是不是选错了通信模式既然波形暴露了MISO上的同步问题我把怀疑方向转向了SPI模式匹配。MT6835的SPI从机接口支持Mode 0和Mode 3两种模式手册里写了空闲时SCLK为低用Mode 0空闲时SCLK为高用Mode 3。我默认选了Mode 0因为绝大多数SPI从机芯片的默认模式就是Mode 0。但有一种情况很特殊某些从机芯片本身是“先输出数据、再采样数据”的时序也就是说它在第一个SCLK边沿之前就把第一位数据放在了MISO上并且要求主机在SCLK的第二个边沿而不是第一个边沿去采样。如果从机的手册里说的是“Data setup time before SCK rising edge”那实际工作模式就是CPHA1而不是我理解的CPHA0。我翻出MT6835手册中SPI时序图仔细数了一下时序参数它明确写了t_SU数据建立时间是在SCLK上升沿之前t_HD数据保持时间是在SCLK上升沿之后。这个定义对应的是CPHA0的时序——主机在SCLK上升沿采样。但我又注意到手册里一个特别不起眼的细节MT6835的MISO数据变化沿是SCLK的下降沿而不是上升沿。也就是说从机在SCLK下降沿更新MISO在下一个上升沿时让主机采样——这个行为正好符合Mode 0的时序关系没有问题。那问题就不在时钟极性上。既不是Mode 1也不是Mode 3Mode 0就是对的。2.4 怀疑时钟频率把SPI主频降到最低软件模拟SPI的频率我已经降到了500kHz比MT6835手册里标称的最大SPI时钟10MHz低得多所以频率本身不是问题。我还做了一组对照实验直接切换回硬件SPI把分频系数调到256让SCLK频率降到250kHz然后重复读寄存器。结果依旧全部是0xFF没有任何改善。到这里时钟频率、时钟极性、数据格式MSB first这些常规SPI参数都被排除掉了。问题显然不是出在“参数配置”这个层面而是出在了更底层的地方。3. 真正的罪魁祸首当GPIO复用到了“调试”而不是“SPI”3.1 CubeMX里的一个隐藏配置引脚功能被默认成了SWD排查进入死胡同时我抱着试试的心态打开CubeMX重新检查了一遍G031的引脚配置。然后我看到了一个此前完全没注意的配置项PB4和PB5默认分配的功能是SWDIO和SWCLK——它们原本属于ARM调试接口不是SPI我们这块板子在设计时为了省事没有接SWD调试口程序都是用ST-Link通过UART或者ICP方式烧录的所以这个默认配置从来没被发现过。而SPI1的SCLK和MISO恰好就在PB3和PB4上CubeMX在生成代码时确实把PB4配置成了SPI1_MISO但PB4的初始功能仍然被保留为SWDIO——这两者在物理上是同一个引脚功能由AFR寄存器Alternate Function Register决定而AFR里可以同时启用多个功能的选择位STM32的每个引脚最多可以映射到16个不同的复用功能。问题就出在这里CubeMX在把PB4从SWDIO重新映射到SPI1_MISO时有可能没有正确清除SWDIO对应的AFR位导致PB4引脚的AFR寄存器里同时保留了SWDIO和SPI1_MISO两条映射路径。硬件上它会优先按哪条路径工作取决于AFR位段的具体值而不同位段的值可能同时生效——这在STM32F0/G0系列上是完全可能发生的。半信半疑之间我调出了生成的代码看到了这样的设置GPIO_InitStruct.Pin GPIO_PIN_4; GPIO_InitStruct.Mode GPIO_MODE_AF_PP; GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_LOW; GPIO_InitStruct.Alternate GPIO_AF1_SPI1; HAL_GPIO_Init(GPIOB, GPIO_InitStruct);从代码本身看PB4被配置成了AF1SPI1这看起来是对的。但再往下看同一份初始化代码里PB3被配置成了AF0系统功能PB5被配置成了AF0。这里就出现了矛盾PB3和PB4做SPIPB5做SPI但AFR里PB3和PB5用的AF0是什么功能翻看G031的参考手册RM0444中的Alternate Function Mapping表PB3的AF0是SWOSerial Wire OutputPB5的AF0是SPI1_NSS片选而PB4的AF0是SWDIO。CubeMX默认给所有引脚都初始化成AF0再对PB4单独设置成AF1。如果AFR寄存器里PB4的AF1位段和AF0位段同时被写成1那硬件行为就无法预测了。从MT6835的MISO一直输出异常毛刺来看PB4引脚确实没有被正确地用作SPI的MISO输入——它极大概率还工作在SWDIO模式。3.2 验证直接操作AFR寄存器绕过CubeMXCubeMX生成的代码我暂时不信任了直接用寄存器操作把PB3到PB5的AFR位段全部显式清零再设置成SPI1对应的功能// 把PB3-PB5的AFR全部清零 GPIOB-AFR[0] ~(0xFFFUL 12); // 清除PB3, PB4, PB5的AFR位段 // 重新配置 GPIOB-AFR[0] | (0x1UL 12); // PB3 - AF1 (SPI1_SCK) GPIOB-AFR[0] | (0x1UL 16); // PB4 - AF1 (SPI1_MISO) GPIOB-AFR[0] | (0x1UL 20); // PB5 - AF1 (SPI1_MOSI)然后重新烧录用硬件SPI读取设备ID。结果还是0xFF。我一度想砸板子。但转念一想直接操作AFR寄存器是对的问题应该不在AFR配置上。3.3 冷静后的重新审视会不会是CS片选时序要求更苛刻我又把事情重新捋了一遍。从软件模拟SPI的波形来看CS低电平持续了整个传输过程约200us然后在最后一字节之后拉高。这个时序很宽松了从机的片选建立时间t_CSSC和片选保持时间t_CSHC都远小于200us。但MT6835手册里可能还有一条我没留意的要求CS的下降沿不能与SCLK的第一个上升沿之间的间隔太短。如果MT6835在CS下降沿之后需要至少几百纳秒的准备时间而我的代码在拉低CS之后立刻开始了字节传输从机可能还没准备好就收到了SCLK导致第一个字节被丢弃后续全部错位。我回去看了一眼代码HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); HAL_SPI_TransmitReceive(hspi1, tx_buf, rx_buf, 2, 100);两行之间没有任何延时。而CubeMX生成的HAL库函数调用本身有一定开销但这个开销不确定可能只有几十纳秒也可能达到几百纳秒。对于普通的SPI从机来说这根本不是问题但如果MT6835的CS建立时间要求超过这个量级第一字节就不可靠。我在拉低CS之后手动加了一个10us的延时HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); delay_us(10); HAL_SPI_TransmitReceive(hspi1, tx_buf, rx_buf, 2, 100); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET);重新编译、烧录、读取。神奇的事情发生了设备ID读出来了——0x5A。3.4 柳暗花明正确读到寄存器值的瞬间0x5AMT6835的设备ID手册上写的就是0x5A。接下来我又连续读了几十个寄存器包括0x01到0x10这一段的配置区和状态区全部都能读到与手册复位值一致的数据。其中一个故障状态寄存器的值也符合预期——芯片上电后无故障值为0x00。这一刻的愉悦感比写通一个完整的电机控制算法还要强烈。因为这不是一个“按手册配置就能跑通”的简单工作而是经过反复排错、最终靠一个“加延时”的小动作解决的问题。但故事到这儿还没完。4. 真正的根因不只是“延时不够”那么简单4.1 为什么延时能够解决“MISO一直为高”的问题如果只是CS建立时间不够现象应该是从机还没准备好主机的第一个字节丢失后续字节错位——但MISO应该还是会被从机驱动出现的是“数据错”而不是“MISO一直为高”。从0xFF到正确值这个变化过程说明加延时的真正作用不是“等从机准备好接收”而是“等MT6835内部的电源或时钟域稳定”。后来我又翻了一遍MT6835的数据手册终于找到了一段此前完全忽略的说明MT6835的SPI从机接口在上电后POR释放后有一段时间处于“内部初始化”状态在这个状态下即使CS和SCLK都有输入SPI接口也不会响应任何传输。手册建议主机应该在MT6835完成内部初始化之后再发起SPI通信而内部初始化的时间取决于外部晶振起振时间和内部LDO稳定时间最坏情况下可能达到数十毫秒。但问题在于——我们的系统里MT6835和STM32G031是同时上电的G031的启动时间远远快于MT6835完成内部初始化所需的时间。G031在主频64MHz下从复位到运行到main函数大约只需要几百微秒而MT6835可能需要几毫秒到几十毫秒才能准备好SPI从机接口。所以在MT6835还没准备好的时候我们的代码就发起了SPI读操作CS被拉低但MT6835的SPI接口还在内部初始化中没有进入正常的从机模式。SCLK进来了但内部SPI状态机没有strobe到数据移位寄存器没被正确触发。MISO引脚此时可能处于高阻或者内部上拉状态对外表现为高电平——也就是我们看到的0xFF。而当我加了10us延时之后虽然这个延时远小于MT6835可能的初始化时间毫秒级但配合HAL_SPI_TransmitReceive本身的执行时间2字节约需要几十微秒加上循环和函数调用开销整段代码从CS拉低到真正完成传输的时间被拉长了。如果恰好跨越了MT6835完成初始化的时间点后续字节就都能正常响应了。后来我干脆做了一个更严谨的实验把10us延时改成了100ms在main函数开头、配置完SPI外设但还未执行任何通信之前先做一次200ms的延时。结果不需要在CS拉低后加延时直接正常发送一次就通过。4.2 关于MT6835片上Flash的一个重大发现在读寄存器数据的过程中我还注意到一个特殊地址0xBFC7FFE0。这个地址不是普通的寄存器地址而是MT6835内部Flash的一个映射区域。当时我在做Flash读取实验时用SPI向MT6835发送了读Flash的命令0x06命令字 三字节地址结果能回读到Flash内容。但有一个奇特现象如果连续快速读取会出现回读数据全部变成0xFF的情况。一开始我以为也是时序问题就把CS拉低后的延时从10us加到了50us——结果没有任何改善。后来才发现这个0xBFC7FFE0地址区域是芯片内部BootROM的影子映射区里面存放着启动代码。当主机通过SPI去读取这个区域时MT6835内部会经历一次总线切换从Flash总线切换到BootROM总线这个切换过程需要时间。如果主机发出的读命令频率过高芯片内部的切换还没完成MISO上就会返回无效数据全0xFF。这个发现给了我一个非常关键的启示SPI通信的可靠性不只是看SCLK频率、CPOL/CPHA匹配、CS时序这些常规参数还要看从机芯片内部是否有需要额外时间的操作。对于这类带片上Flash和BootROM的SoC很多情况下我们以为的“SPI通信失败”其实是主机的速度超过了从机的内部状态机切换速度。4.3 为什么第一次用软件模拟SPI会读到“看似正确”的数据回到前面那个让我白白高兴了半小时的“0xA5, 0x5A”。现在回头分析软件模拟SPI那次并不是真的通信成功了。当时我虽然在CS拉低和字节传输之间也加了延时但这个延时不够长只有几百纳秒导致MT6835的SPI接口没进入稳定状态。MISO上出现的那几个毛刺就是内部状态机在切换时的瞬态信号经过引脚缓冲电路后传递到了外部。逻辑分析仪采样到的0xA5、0x5A其实是随机的毛刺捕获不是寄存器数据。这个教训非常深刻只要从机没有正确应答一切看似合理的回读数据都不可信。尤其当回读值在每次实验之间还不一样的时候更要怀疑是信号完整性或从机初始化的问题而不是“SPI参数配置错误”。4.4 为什么直接操作AFR寄存器之后现象没有立刻改变这个问题当时也让我困惑但后来想明白了AFR寄存器确实需要正确配置但它只影响引脚的功能映射不影响从机的启动时间。当时我直接操作AFR寄存器后CS拉低到传输之间没有加延时所以从机根本没准备好自然还是0xFF。换句话说AFR寄存器错误和从机启动未完成是两个独立的问题而我把它们误当成了一个。第一次用CubeMX时PB4的AFR设置确实可能是错的但也不确定我手动修正了AFR之后第二个问题从机初始化时间又成为瓶颈。只解决一个问题是不够的必须把两个问题同时解决才能跑通。这个经验告诉我一个排查SPI通信问题的核心方法把所有可能的原因列成清单逐一验证排除不要指望一次修改就能解决所有问题。5. 复盘给所有SPI初学者和经验丰富工程师的调试自查清单5.1 一套通用的SPI通信排查顺序经过这次折腾我把自己过去几年积累的SPI调试经验整理成了一个排查清单按优先级排好。遇到SPI通信失败不要急着用逻辑分析仪狂抓波形先过一遍清单确认从机电源和时钟正常用万用表量VDD用示波器看晶振起振波形。很多“SPI通信失败”其实是芯片压根没工作。确认CS、SCLK、MOSI、MISO四根线没有接错或虚焊用万用表蜂鸣档打一下通路特别是MISO——如果从机的MISO没接到主机的MISO而是接到了MOSI现象就是主机能发不能收。确认主机和从机的SPI模式CPOL/CPHA匹配绝大多数从机用Mode 0或Mode 3查手册时序图特别注意数据变化沿和采样沿。降频测试把SCLK频率降到手册标称最大值的1/10甚至1/100排除信号完整性和从机速度问题。检查CS时序从机是否有CS建立时间要求主机拉低CS后是否给了足够的延时传输完成后CS保持低电平的时间是否满足要求确认从机的内部初始化时间如果是带片上Flash、BootROM或复杂状态机的SoC第一次上电后要有足够的初始化时间。最好的做法是上电后延时几百毫秒再操作。用软件模拟SPI做边界测试如果硬件SPI失败可以用GPIO模拟SPI但要注意软件模拟SPI会放慢速度可能会掩盖真正的问题比如时序匹配问题也可能会因为速度变慢而“意外成功”。软件模拟SPI成功后不要急着高兴要想想为什么硬件SPI不行。检查GPIO复用功能配置STM32系列引脚复用是个大坑尤其是那些同时承载SWD、JTAG、UART、SPI功能的引脚。用CubeMX生成代码后建议检查一下AFR寄存器的值确保引脚确实映射到了目标外设。5.2 MT6835参数速查表基于本次调试实测参数值说明设备ID寄存器地址0x00复位值0x5ASPI支持模式Mode 0 / Mode 3实测Mode 0稳定最大SCLK频率10MHz建议实际使用不超过5MHzCS建立时间t_CSSC手册未明确标注实测100us以上最稳上电初始化时间手册未明确标注实测建议至少等待100ms读命令字0x06后跟寄存器地址写命令字0x02后跟寄存器地址和数据Flash读取0x06 3字节地址读取0xBFC7FFE0区域时需注意内部总线切换时间提示以上参数基于我这块MT6835样片的实测不同批次或封装的芯片可能存在差异。拿到芯片后建议先做一轮系统性的寄存器读取测试确认复位值是否与手册一致。5.3 一个值得留意的设计改进上电时序控制经过这次调试我在自己的项目模板里加了一个统一的上电延时逻辑void system_delay_for_slave_ready(void) { // 等待MT6835完成内部初始化 // 实测100ms足够覆盖外部晶振起振 LDO稳定 BootROM加载 HAL_Delay(200); }在main函数开头调用一次然后才进入SPI和其他外设的初始化流程。这个延时对于大多数SPI从机来说是多余的但对于MT6835这种带完整启动流程的SoC来说多等200ms的成本完全可以接受换来的是通信稳定性的大幅提升。另外我还习惯在CS拉低之后加一个至少10us的软件延时再发起SPI数据传输#include main.h void mt6835_cs_low(void) { HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); delay_us(10); } void mt6835_cs_high(void) { delay_us(10); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); }这里之所以不在CS拉高后立即结束是因为某些从机在CS上升沿之后还有内部锁存操作过早地让CS浮空或拉高可能会导致最后一位数据没锁存成功。6. “见鬼”调试的终极教训不靠谱的“默认值”是最大的坑这次调试过程里我犯了好几个错误每一个单独拿出来都够写一篇笔记的。最有价值的教训放在最后说不要相信任何“默认配置”在特定芯片上一定是对的。具体到这次案例里三个“默认”叠加导致了我两天半的反复折腾第一默认STM32的引脚复用配置是对的。CubeMX是自动生成的代码但不代表它生成的AFR值总是符合实际需求。尤其是PB4这种默认承载SWDIO的引脚在做SPI映射时要特别小心。第二默认MT6835上电后SPI接口立即可用。实际上它有自己的启动初始化流程在这个流程完成之前发数据从机根本不会理会。这种“看不见的初始化时间”在带Flash、带BootROM、带DSP核的芯片上非常常见。第三默认“软件模拟SPI调通硬件SPI参数有问题”。实际上软件模拟SPI只是因为降低了速度、恰好躲开了从机未初始化的问题并不是真的验证了SPI模式配置正确。这三点叠加在一起让一次本可轻松的联调变成了“见鬼的调试”。解决问题的钥匙也很简单拉长CS建立时间、增加上电等待延时、主动核对AFR寄存器。写到这里再补充一个这次调试过程中的副产品——一个调试SPI从机时非常实用的小技巧如果一个SPI从机芯片偶尔能通信成功、偶尔失败且失败的规律是“复位后第一次操作必失败”那大概率就是芯片上电初始化时间的问题。这时直接在代码开头加一个长的无条件延时比如500ms比什么抓波形、调极性都管用。MT6835现在已经在我们产品上稳定跑了两周每天24小时不间断运行SPI通信一次故障都没出过。回看当初差点换芯片的冲动只能说SPI通信里没有真正的“见鬼”一切现象都有明确的物理原因只是我们还没找到它而已。