ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

嵌入式SPI通信协议实战:原理、时序模式与调试技巧

嵌入式SPI通信协议实战:原理、时序模式与调试技巧 我做嵌入式这几年跟各类通信协议打交道的次数数都数不清。串口、I2C、CAN、USB各有各的脾气但要论“明明波形看着没问题设备就是死活不应答”的体验SPI通信协议排第二没人敢排第一。它看似就四根线节奏却极其敏感一个模式配错、一个片选时序不到位就能让你在示波器前怀疑人生。这篇文章不是教科书式科普而是把我自己调屏幕、调Flash、调传感器时积累的SPI经验完整梳理一遍。你会看到SPI的核心原理到底是什么、四种工作模式怎么一劳永逸地搞定、硬件片选和软件片选该怎么选还会带上STM32驱动ST7789屏和SD卡共享总线的完整实操记录以及FPGA侧实现SPI Slave的思路。适合正在学单片机和嵌入式协议的学生、做显示或存储方案的工程师以及想在FPGA上写外设协议的朋友。1. 先把SPI通信协议当成一个“双向移位寄存器游戏”1.1 四根线分别干什么先说最基础的部分因为我在社区里见过太多接线接反的例子。SPI全称是Serial Peripheral Interface串行外设接口最早由Motorola提出。它本质上是一套主从结构的同步串行总线常见形态是四根线引脚方向作用SCLK主设备输出时钟信号决定每一位的节奏MOSI主设备输出Master Output Slave Input主发从收MISO从设备输出Master Input Slave Output从发主收CS / SS主设备输出片选信号通常低电平有效选中某个从设备接线时最容易被新手搞混的是MOSI和MISO。记住一个原则SPI的设备端引脚名都是站在“自己”角度写的主设备的MOSI必须接从设备的MOSI不是交叉接。你去翻W25Q256的数据手册它的引脚定义里明明写着DIData In接主设备的MOSIDOData Out接主设备的MISO。如果按UART那种“TX接RX”的思维去接SPI读回来的数据大概率是0xFF或者全0。CS信号则是整个通信的“门卫”。CS为高时从设备直接无视SCLK和MOSI上的任何变化CS拉低从设备才开始听时钟、收数据。一主多从场景下每个从设备独占一根CS线主设备通过把对应CS拉低来选择跟谁说话。1.2 为什么SPI一定要有时钟信号SPI是同步串行协议SCLK由主设备产生从设备不产生时钟只需要被动接收。这一点和UART有本质区别UART是异步通信双方得事先约定好波特率误差一大就开始乱码SPI则不需要主设备把时钟发过去从设备跟着时钟边沿采集数据就行。这个特性带来的好处是SPI可以做到非常高的速率。常规MCU上几MHz到几十MHz都很常见工业级设备跑到上百MHz也不稀奇。坏处也很明显通信双方必须严格在时钟边沿对齐任何一个多余的时钟脉冲或者漏掉的脉冲都会让数据整体错位。我习惯用一个比喻理解SPI它就像两个人用一根绳子传球主设备负责喊节拍SCLK喊一声传一位。只要节拍稳定传多少位都能对上要是节拍乱了后面全部白搭。1.3 一次通信的数据交换过程很多人初学SPI时容易有一个误区觉得“读”和“写”是两件独立的事。实际上SPI一次传输同时完成读和写因为它的底层就是主从两个移位寄存器首尾相连。假设主设备和从设备各有一个8位移位寄存器。SCLK每跳一个有效边沿主设备把自己寄存器最高位移到MOSI线上同时从MISO线采入从设备寄存器的最高位从设备也是如此把自身寄存器最高位移到MISO再从MOSI采入主设备的位。连续8个时钟后主设备发出去的8位数据进入从设备的寄存器从设备的8位数据也完整进入了主设备的寄存器。所以SPI没有纯粹的“读”操作。你想从W25Q256读数据必须先发送一条读命令比如0x03再继续提供时钟从机会在这期间把数据送到MISO上。你看到的“读”本质上是“边发边收”。反过来如果只想写数据接收到的内容直接丢弃即可不影响通信。调试时这个特性特别有用读一个从机ID之前你可以用0xFF去喂时钟因为0xFF是全1不影响大多数设备的命令解析还能顺便推进时钟。这也是为什么很多逻辑分析仪抓SPI报文时MISO上的有效数据旁边总跟着一堆0xFF。2. SPI时序的核心CPOL和CPHA搞懂它才不算入门2.1 时钟极性CPOL是什么SPI最劝退新手的就是四种工作模式Mode 0、Mode 1、Mode 2、Mode 3。这四种模式由两个参数决定CPOL和CPHA。CPOL是时钟极性规定SCLK空闲时是高电平还是低电平。CPOL0SCLK空闲为低时钟一启动就产生上升沿CPOL1SCLK空闲为高时钟一启动就产生下降沿。一句话CPOL只要看总线空闲时SCLK停在哪个电平就行。2.2 时钟相位CPHA是什么CPHA是时钟相位规定数据在SCLK的第几个边沿被采样。CPHA0表示在第一个跳变沿采样数据CPHA1表示在第二个跳变沿采样数据。这里说的“第一个”“第二个”是相对每个bit的传输周期而言的。一个bit周期内有上升沿和下降沿两个边沿如果CPHA0就利用第一个边沿来采数据如果CPHA1就等第二个边沿才采。另一个边沿通常用来让发送方切换数据保证数据有足够的建立时间。把CPOL和CPHA组合起来就是四张完全不同的时序图。很多从设备数据手册里会给出支持哪几种工作模式但不熟悉的工程师经常会忽略这一步直接用默认的Mode 0去调结果读出来的数据全是乱的这是最常见的SPI调试事故之一。2.3 四种模式的对照表与识别技巧我整理了四种模式的核心差异方便你直接对照模式CPOLCPHASCLK空闲电平数据采样边沿数据切换边沿Mode 000低上升沿下降沿Mode 101低下降沿上升沿Mode 210高下降沿上升沿Mode 311高上升沿下降沿记住一个关键结论Mode 0和Mode 3在嵌入式里最常见大部分Flash、LCD、SD卡都支持这两种模式。Mode 1相对少见Mode 2更少见。如果你拿到一个新设备不知道它支持哪种模式优先从Mode 0或Mode 3开始试命中率非常高。实际识别模式的技巧有两种。第一种是用示波器抓SCLK和MOSI看空闲时SCLK是高是低立马确定CPOL再看数据变化发生在上升沿还是下降沿以及采样点相对数据稳定位置的关系就能确定CPHA。第二种是直接查设备的datasheet通常会有“时序特性”一节里面明确写了“Supports SPI Mode 0 and Mode 3”之类的描述。2.4 常见设备的模式习惯我把自己接触过的设备模式放在这里供参考省得你一个个去查W25Q系列SPI Flash支持Mode 0和Mode 3默认用Mode 0完全没问题。ST7789、ILI9341等TFT屏幕驱动一般用Mode 0部分屏也能跑Mode 3。SD卡SPI模式支持Mode 0和Mode 3上电初始化阶段对时序要求严格模式必须配对才能握手成功。ADS1256高精度ADC典型Mode 1很多人默认Mode 0调它结果DRDY信号都对但就是读不到数据。L3GD20陀螺仪常见Mode 3我记得有一段时间我总被这种传感器卡住最后发现就是CPOL配反了。这里补充一个自己的经验模式不对的典型故障现象不是“完全没数据”而是“数据偶尔对、偶尔错”或者“第一个字节对后面全部乱”。因为你发送命令时如果命令高位碰巧在采样点上是稳定的第一个字节可能侥幸正确但后续数据一旦需要在切换边沿采样就会全面崩溃。所以遇到这种诡异现象先从模式查起。3. 硬件片选与软件片选这个选择直接影响项目稳定性3.1 硬件NSS看起来省事实战中却容易翻车SPI通信中CS信号虽然简单但它在时序上极其挑剔。很多从设备要求CS拉低后至少等一段时间才能给时钟建立时间传输结束后CS拉高也不能太早保持时间否则命令会被丢弃或者最后一个字节写不进去。硬件片选Hardware NSS的意思是由MCU的SPI外设自动控制NSS引脚你只管往数据寄存器里写数据CS的拉低拉高由硬件完成。听起来很省心但实际项目中我吃过不少亏。一个典型问题是在某些MCU上DMA连续发送时硬件NSS会在字节间隙瞬间释放再拉低这个毛刺对很多从设备来说是致命的会导致整包数据被拆成多段Flash写入直接失败。另一个问题是多从机场景。硬件NSS通常只对应一个特定的外部引脚当你需要挂多个从设备时MCU并没有那么多硬件NSS脚给你用最后还得靠GPIO软件模拟。既然终究要软件控制那不如一开始就用软件片选。3.2 软件片选的优势软件片选就是用普通GPIO去控制每个从设备的CS引脚想拉低就拉低想拉高就拉高中间加多少延时都随你。这个方案的优势很明显。第一CS的建立时间和保持时间完全可控遇到从设备要求“CS拉低后延时2us再发时钟”这类变态需求直接在代码里加上就行。第二一主多从非常方便每加一个设备就多分配一个GPIO逻辑清晰。第三当你需要动态切换总线速率时可以先把所有CS都拉高改完分频系数再拉低下一个设备的CS避免在通信过程中切换导致从设备解析错乱。我目前做的项目几乎全部采用软件片选。哪怕只挂一个从设备我也会用GPIO控制CS不为别的就为灵活。说实话硬件NSS能省下的工作量极其有限但它引入的时序风险却可能让你在调试时多花好几天。3.3 什么时候才适合用硬件片选硬件片选也不是完全没用。它适合那些对CS时序要求极其宽松的从设备比如某些简单的移位寄存器芯片CS只要大致对就行中间抖一两下也无所谓。另一种场景是你需要把SPI外设和DMA深度绑定希望由外设自动控制CS从而减少CPU介入这种情况下硬件NSS的自动化价值才能体现出来。但即便如此我也建议前期调试时先用软件片选把功能跑通确认设备通信没问题之后再决定要不要切换到硬件片选。这样分层推进出现问题时排查范围小得多。4. SPI、I2C、UART怎么选别再把它们混为一谈4.1 三者的核心差异对照表做嵌入式方案选型时SPI、I2C、UART经常被摆在一起比较。我见过不少开发者因为选错总线导致产品性能达不到预期或者代码复杂度暴涨。这三者的差异其实很清晰特性SPII2CUART引脚数量4根起每加一个从设备多一根CS2根SDASCL2根TXRX通信方向全双工半双工全双工时钟来源主设备产生SCLK主设备产生SCL双方各自约定波特率最高速率几十MHz甚至更高标准100kHz快速400kHz高速3.4MHz受限于双方时钟精度常规几Mbps应答机制无硬件应答每字节有ACK/NACK无硬件应答多设备挂载每个从设备一根CS通过7位/10位地址区分点对点为主少数多机协议另行实现典型场景Flash、LCD、ADC、高速传感器低速传感器、EEPROM、RTC调试串口、无线模块、工控设备I2C速度上不去的核心原因是它使用开漏输出加外部上拉电阻所有设备共用两根线推高电平全靠上拉电阻慢慢充速率自然受限。SPI是推挽输出信号翻转快所以能跑到非常高的频率。4.2 按项目场景选型的经验我的选型建议可以浓缩成三条。第一如果数据量大、实时性要求高直接选SPI。典型例子是彩色LCD屏的刷新一帧240x240的16位色图就有115200字节用I2C的400kHz去刷一帧要好几百毫秒根本没法用。SPI跑10MHz一帧也才一百多毫秒跑40MHz立刻流畅起来。第二如果挂载的传感器多、每个传感器数据量小、带宽要求低优先选I2C。I2C最大的优势是接线少一条总线上能挂几十个设备每个设备有自己的地址MCU用两根引脚就能管理一大片传感器。代价是软件复杂度略高需要处理地址、ACK、总线仲裁。第三如果通信距离超过几十厘米又或者要跟PC、无线模块对接UART是更稳妥的选择。UART在长距离传输时可以通过RS232、RS422、RS485转成差分信号抗干扰能力远好于SPI和I2C。顺带提一句很多MCU的USART可以配置成SPI模式这在引脚紧张时是个小技巧但功能上一般不支持DMA或者只支持半双工只能应对简单场景。5. 实操案例STM32用SPI驱动ST7789屏和SD卡5.1 CubeMX里的SPI参数怎么配我以STM32F103C8T6为例讲一下在CubeMX里配置SPI的核心步骤。STM32CubeMX的优势是可以图形化看到引脚分配也能很直观地设置SPI参数但对参数的含义讲得不多所以很多人配置完还是懵的。进入SPI1的配置界面后Mode选择Full-Duplex MasterHardware NSS选择Disable。这个很关键因为我前面说了实际项目里我会用软件片选所以NSS直接关掉CS引脚分配一个普通GPIO就行。参数区有几个关键项Baud Rate Prescaler分频系数。STM32F103的SPI1挂载在APB2上外设时钟是36MHz。分频系数可选2、4、8、16等选8就是36/84.5MHz。Clock Polarity和Clock Phase就是前面讲的CPOL和CPHA按从设备手册设置。驱动ST7789我一般选Low、1st Edge也就是Mode 0。Data Size选8Bit绝大多数SPI设备都是8位数据宽度。First Bit选MSB FirstSPI协议规定先传最高位这个不要改。这里要特别说明一下时钟频率问题。很多人以为CubeMX里填的“Baud Rate”就是实际SPI速率其实不对CubeMX里只能选分频系数实际速率要靠你自己算。以F103为例SPI1的外设时钟来自APB2APB2可能跟系统主频72MHz不一样实际是36MHz。你选了8分频那么SPI速率就是4.5MHz。如果看着CubeMX里的参数以为跑的是9MHz甚至18MHz后面调试时对不上就无从下手。5.2 ST7789的初始化与刷屏流程ST7789是一款很常见的240x240 IPS屏驱动芯片接口支持4线SPI外加DC数据/命令选择和RESET引脚。每一帧数据量约115200字节必须用DMA才能刷得流畅。初始化流程一般是复位引脚拉低再拉高然后发送一串初始化命令比如0x36设置扫描方向0x3A设置像素格式为16位0x21开反色有的屏需要最后0x29打开显示。ST7789的初始化命令序列在数据手册里写得很清楚各家屏厂的驱动代码也能直接参考这里我不逐个命令展开但有一个重点必须提醒ST7789区分命令和数据靠的是DC引脚不是靠CS。很多人调用SPI驱动时只控制CS忘记DC结果屏幕要么白屏要么显示花屏。刷屏流程也不复杂。先把显示窗口设置到整个屏幕区域然后把RAM地址写指针归零之后就是连续往SPI发送像素数据。发送时DC要拉高CS保持低电平期间不要频繁拉高CS。一次完整的刷屏事务内CS应该从数据发送开始一直保持到最后一字节结束。5.3 SD卡和屏幕共享SPI总线的三个坑很多跑图形界面的项目会同时用到LCD和SD卡而STM32的SPI外设往往数量有限所以让两个设备共享同一条SPI总线是很常见的做法。ST7789接SPI1的MOSISD卡也接同一根MOSI各自CS分开逻辑上没问题但实践中有三个坑。第一个坑是SD卡初始化时序要求非常苛刻。SD卡进入SPI模式之前主机必须以不超过400kHz的时钟发送至少74个SCLK脉冲且CS保持高电平。很多人在CubeMX里把SPI配置成4.5MHz后直接去初始化SD卡结果CMD0发出去毫无反应就是因为上电速率太高。解决办法是初始化阶段把SPI时钟降到400kHz以下初始化完成后再切换到高速。第二个坑是两个设备的CS必须严格互斥。屏幕在刷新的时候SD卡的CS引脚必须稳定为高。如果因为程序时序问题屏幕的DMA传输还没结束就误拉低了SD卡的CSSD卡会误认为主机发来新命令轻则返回错误重则导致文件系统挂载失败。第三个坑是动态切换SPI速率必须小心。因为两个设备共享总线而屏幕能跑4.5MHz甚至更高SD卡却要求初始化时低速。一种方案是在屏幕上电前先完成SD卡初始化然后把总线切到中速另一种方案是在每次切换设备时重新配置分频系数。我的经验是尽量减少切换次数最好固定一个妥协速率比如2MHz两个设备都稳定代价只是刷屏速度略慢。5.4 发送数据与片选操作的一段实用代码下面这段代码是我的一个精简模板思路是软件片选加HAL库的TransmitReceive。注意ST7789需要额外处理DC引脚。void LCD_CS_LOW(void) { HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET); } void LCD_CS_HIGH(void) { HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET); } void LCD_DC_CMD(void) { HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_RESET); } void LCD_DC_DATA(void) { HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_SET); } uint8_t SPI_TransferByte(uint8_t txData) { uint8_t rxData 0; HAL_SPI_TransmitReceive(hspi1, txData, rxData, 1, 100); return rxData; } void LCD_WriteCommand(uint8_t cmd) { LCD_DC_CMD(); LCD_CS_LOW(); SPI_TransferByte(cmd); LCD_CS_HIGH(); } void LCD_WriteData(uint8_t data) { LCD_DC_DATA(); LCD_CS_LOW(); SPI_TransferByte(data); LCD_CS_HIGH(); }这段代码对于初始化命令和少量数据完全够用。但要注意刷屏时不要用这个函数逐字节发送否则CPU全耗在等待标志位上了。正确的做法是一次性把CS拉低、DC拉高然后用HAL_SPI_Transmit_DMA或者直接调用HAL_SPI_Transmit发送整块像素缓冲区发送完成中断里再把CS拉高。有个细节值得注意使用HAL_SPI_Transmit时函数返回并不代表最后一个字节已经从SCLK上完整移出它只代表数据已经交给SPI外设。如果紧接着就把CS拉高某些对时序敏感的设备会丢失最后一个字节。稳妥的做法是在发送完成后轮询SPI的TXE和BSY标志或者通过HAL回调判断传输真正结束再释放CS。6. 想用FPGA实现SPI抓住状态机就够6.1 最简SPI Slave的状态机思路如果你在做FPGA经常会遇到需要自己写SPI接口的情况比如把FPGA当作一个SPI从设备挂到MCU下面。这个任务本质上不复杂核心就是状态机加移位寄存器。最朴素的SPI Slave由几个基本部分构成。第一步检测CS下降沿作为一次传输的开始第二步用SCLK作为位时钟每个有效边沿把MOSI采样进一个移位寄存器同时把准备好的输出数据放到MISO第三步等CS上升沿到来把接收到的字节锁存进输出寄存器同时更新状态准备下一次传输。我见过很多新手在这步犯的最常见错误是把系统时钟当采样时钟直接用高频系统时钟去“数”SCLK的边沿。这种设计在低速时或许没问题但一旦SCLK频率较高就会因为计数精度不足导致数据错位。更稳妥的做法是直接用经过同步的SCLK驱动移位寄存器系统时钟只负责跨时钟域处理和状态更新。6.2 跨时钟域处理和常见bugFPGA接收外部SCLK时SCLK相对于FPGA内部时钟是异步信号直接使用有可能会产生亚稳态。我习惯先把SCLK和CS信号各打两拍再做边沿检测。下面是一个典型的边沿检测代码片段。reg sclk_r1, sclk_r2; always (posedge clk) begin sclk_r1 SCLK; sclk_r2 sclk_r1; end wire sclk_rising sclk_r1 ~sclk_r2; wire sclk_falling ~sclk_r1 sclk_r2;拿到边沿脉冲后根据SPI模式决定哪个边沿采样MOSI、哪个边沿更新MISO。比如在Mode 0下上升沿采样那就用上升沿把MOSI移入寄存器用下降沿把要发送的数据放到MISO上这样MISO有半个时钟周期的稳定时间供主机采样。另一个常见bug是CS释放时状态机没有复位。如果CS已经拉高但状态机还在继续移位下一帧数据到来时计数器就不是从0开始所有位都会错位。解决方法是把CS信号也同步到系统时钟域在检测到CS高电平时强制状态机回到IDLE。MISO的三态处理也容易被忽略。多个从设备共享一条MISO总线时FPGA作为从设备在CS无效期间必须把MISO置为高阻否则会拉着总线跟其他设备冲突。这个可以在逻辑里用一个三态缓冲实现CS有效时输出移位寄存器最高位CS无效时输出1bz。7. 实际调试中遇到的典型问题和排查方法7.1 读回的数据永远是0xFF或0x00这是SPI调试里最常见的疑难杂症。遇到这个现象不要急着改代码先按照下面的顺序排除问题。先检查供电和复位。很多SPI从设备都是高电平复位如果复位引脚一直被拉低芯片永远处于复位状态MISO一直高阻主设备读回来就是0xFF。再检查CS有没有真正拉低如果你用的是软件片选可以在CS拉低后用万用表量一下引脚电平我遇到过GPIO复用配置错误导致CS始终输出高的情况。接着检查MOSI和MISO有没有接反这是接线层面最容易出的问题。最后检查SPI模式读0xFF这个现象尤其容易出现在CPOL/CPHA配置错误时因为主机和从机的采样点完全错开从机收到的命令根本不是你以为的那个值。7.2 第一次通信正常第二次就乱码这种“一次灵、二次不灵”的问题我在调W25Q256的时候遇到过很多次。大多数原因是CS释放时机不对。发送完最后一个字节后如果SCLK还没有完全停稳就拉高CS从设备可能把不完整的字节也算进去导致内部状态错乱。解决办法是发送完成后等待SPI总线进入空闲状态再释放CS。还有一种可能是每个字节之间都拉高了CS。有些SPI设备支持“单字节事务”但很多设备要求一整条命令期间CS必须持续为低。比如Flash的读命令你要连续发命令地址和读取数据CS只能在整条事务结束后拉高。如果你在发送每个字节之间都释放了CS从设备就会认为这是多次独立的短命令返回的数据自然不对。7.3 波形“看起来”没问题设备就是不认逻辑分析仪抓到波形和理想波形一致但设备就是不干活这种情况往往是“看着对实际采样点不对”。一是逻辑分析仪的采样率不够抓高速SPI时采出的波形存在假边沿建议采样率至少是SCLK的8倍以上。二是分析仪的通道延迟或探头电容影响导致你看到的边沿和实际硬件上的边沿有偏差。此外还要检查从设备除了SPI引脚之外的控制引脚是否配置正确。很多SPI屏幕除了CS和DC之外还有RESET引脚很多传感器还有RDY、INT等引脚这些引脚的初始化代码一旦遗漏SPI的波形再完美也白搭。我见过一个项目屏幕花屏的原因是DC引脚被初始化成了复用功能导致它一直在自动翻转数据命令乱套。7.4 高速模式下数据错位怎么办同样的代码低速一切正常一提高分频系数就出现偶发错位这是典型的信号完整性问题。SPI的推挽输出沿很陡长走线加上分布式电容会让波形产生过冲和振铃采样点稍偏就会采到不稳的数据。解决思路是按优先级来先把线长缩短尤其是SCLK和MOSI这两根关键信号线然后在时钟线上串联一个22到33欧姆的电阻抑制过冲如果总线上挂了多个设备检查一下MISO有没有设备冲突。最后再考虑降低SPI速率很多时候性能瓶颈不在协议而在PCB布局。我整理一个简易排查表方便你随手查阅现象大概率原因排查顺序读回全0xFF从设备未工作、CS无效、MOSI/MISO接反、模式错误供电复位 → 电平测量 → 接线 → 模式读回全0x00从设备损坏或CS被持续拉低先用示波器确认CS电平偶发数据错位信号完整性问题减短走线 → 串电阻 → 降速每次上电第一批数据正常后续异常初始化时序不完整CS释放过早加延时等BSY/TXC再拉高CS帧率上不去SPI速率被分频卡死确认APB时钟源调整分频系数写在最后的几句实在话我不是反对用CubeMX的图形化配置也不是说硬件NSS就一定不能用而是想强调一点SPI通信协议的上手门槛远比想象中低但把“能用”变成“稳定用”依赖的是对时序细节的敬畏。我自己的习惯是拿到一个新SPI设备先从数据手册里找到时序图用笔标出采样边沿、数据建立时间和CS的建立/保持时间再决定用哪套配置。调试时永远先低频跑通再用低速逻辑分析仪抓完整报文确认每个字节都正确之后才逐步提高速率、引入DMA。这套流程看起来慢实际却是省时间最快的路。最后再分享一个小技巧SPI出问题时别急着怀疑代码。先把CS、SCLK、MOSI、MISO四根线在空闲状态下的电平量一遍再抓一次真实通信波形对比数据手册八成以上的问题都能在这一步水落石出。踏踏实实把时序看懂了后面无论驱动屏幕、Flash还是FPGA外设都会顺畅得多。
返回列表