这篇来说SPI的解析,下面先看我理解的理论。
SPI(Serial Peripheral Interface)是一种串行通信协议,通常用于微控制器和外设之间的数据交换。SPI通常包括四根线:片选(CS,Chip Select)、主设备输出从设备输入(MOSI,Master Out Slave In)、主设备输入从设备输出(MISO,Master In Slave Out)以及时钟(SCK,Serial Clock)。
SPI支持全双工通信,但通常配置为一主多从模式。
测试平台主要是用的STM32F4的开发板,所使用的示波器是泰克的。
下面看一下连线,因为测试SPI屏,所以线接的有一些乱。
上电后,抓到的波形如下:
主代码,为了看的清楚,我特意注释了一下:
// 调用函数获取SPI Flash设备的ID,并存储在IDNUMS变量中 IDNUMS = W25QCheck(); // 初始化CAN通信模块 CANInit(); // 初始化远程输入模块,设置频率为1MHz,周期为10ms REMOTEInit(167,10000); // 通过串口打印SPI Flash设备的ID printf("\r\nTHE ID OF W25Q IS: %X",IDNUMS); printf("\r\n"); // 设置LCD上显示的颜色为红色 POINT_COLOR = RED; // 在LCD上显示字符串"SPI TEST" LCD_ShowString(30,40,200,24,24,"SPI TEST "); // 在LCD上显示字符串"@lanning" LCD_ShowString(30,70,200,16,16,"@lanning "); // 在LCD上显示操作说明,包括按键KEY0用于写入数据,按键KEY1用于读取数据 LCD_ShowString(30,100,300,16,16,"KEY0 : write KEY1 : read"); // 清空SPI Flash的指定页(这里清空的是第一页) W25QErasePage(0x000000); // 主循环 while(1) { // 延时10毫秒 delay_ms(10); // 扫描按键状态 KEY = KEY_Scan(0); // 再次检查SPI Flash设备ID,可能是为了监控设备是否在线 IDNUMS = W25QCheck(); // 如果按下KEY0键 if(KEY == KEY0_PRES) { // 向SPI Flash写入数据,从Databuf缓冲区开始,写入到地址0x000000,长度为len W25QWrite(Databuf, 0x000000, len); // 在LCD上显示"write finished!"字符串 LCD_ShowString(30,120,300,16,16,"write finished!\r\n"); // LED1状态翻转,可能用于指示正在写入数据 LED1 = ~LED1; // 发送CAN消息 CANSendmessage(sendbuffer); // 延时1毫秒 delay_ms(1); // 接收CAN数据 MDCL = CANReceiveData(receivebuffer); // 打印接收到的CAN数据 for(i = 0; i < MDCL; i++) { printf("the num %d is %x\r\n", i+1, receivebuffer[i]); receivebuffer[i] = 0; // 清空缓冲区,为下一次接收做准备 } } // 如果按下KEY1键 if(KEY == KEY1_PRES) { // 从SPI Flash读取数据到receivebuf缓冲区,从地址0x000000开始,长度为len W25QRead(receivebuf, 0x000000, len); // 在LCD上显示读取到的数据 LCD_ShowString(30,120,300,16,16,receivebuf); // 通过串口打印读取到的数据 printf(receivebuf); printf("\r\n"); // LED1状态翻转,可能用于指示正在读取数据 LED1 = ~LED1; } // 扫描遥控器输入 key = REMOTEScan(); // 如果扫描到遥控器输入 if(key) { // 在LCD上显示"THE KEY VALUE:"和"THE KEY NUMS:"字符串 LCD_ShowString(30,140,200,16,16,"THE KEY VALUE:"); LCD_ShowString(30,160,200,16,16,"THE KEY NUMS:"); // 在LCD上显示按键值和按键次数 LCD_ShowNum(140,140,key,3,16); LCD_ShowNum(140,16
框架:
调试时波形图:
总结:
说一下当时出现的问题点。
SPI容易产生的问题点主要可以总结为以下几个方面:
接线错误:这是最常见的问题之一。SPI通信涉及MOSI、MISO和SCK等多条线路,如果这些线路接错或交叉连接,会导致通信失败。主机的MOSI和从机的MOSI应该相连,而不是交叉相连,同样MISO也是如此。因为当时同事硬件的不是很厉害,一直调不出来,所以我当时给了一点帮助。
配置问题:主机和从机之间的时钟极性(CPOL)和采样方式(CPHA),以及数据发送顺序(高位先行或低位先行)必须设置一致。通信双方如果不是特别细心和理解可能会对数据产生差异,导致数据不一致。
NSS(片选信号)控制不当:对于NSS硬件模式的从机,主机在发送时钟信号前必须确保从机的NSS引脚被拉低,通信结束后需要等待Busy信号后再拉高NSS。如果NSS控制不当,可能会导致从机接收的时钟脉冲数量不正确,进而影响到数据的接收。我给他的意见就是NSS就是个片选信号,要拉的就时候就要拉,不能犹豫。
初始化顺序问题:主机在SPI初始化时不应立刻使能SPI,而应在NSS拉低后再使能。否则,从机可能会少接收一个时钟脉冲,导致接收的数据出现错误,这个问题很着键,当初我自己写的时候也是一下不使能了,没有来个先后顺序。
软件配置问题:有时,主机可能不使用NSS引脚,而是选择使用任意的GPIO普通引脚来片选从机。在这种情况下,如果软件配置不当,没有正确设置NSS为软件模式,或者对NSS的处理不当,都可能导致通信问题。
硬件故障:SPI通信的硬件故障,线路断裂、接触不良或芯片损坏等,也可能导致通信问题,特别是屏的排线,还有杜邦线,上一秒用的还好好的,结果只是换了个位置就不通了。