学过单片机的朋友,对于SPI通信应该比较熟悉吧。小秋也差不多,自学过野火的视频教程,也看过正点原子的视频教程,但是,小秋总感觉自己没有掌握,比如新上手一个SPI通信的芯片,关于SPI的程序还是那么陌生,不知道各位有没有这种类似的情况。现在,小秋由表及里,认真剖析一下SPI,看看SPI通信到底哪点不好掌握。
拿到一块SPI通信的芯片,首先是要解决单片机跟这块芯片的通信问题,能读能写;其次是对这块芯片进行寄存器设置。明确知道了这两个步骤之后,小秋就明白了,为什么小秋之前不能够熟练的掌握SPI。以前把这两个步骤混合起来了,寄存器设置比较麻烦,需要看其datasheet,而且还是英文的,所以如果以上两个步骤一起做,就总觉得很困难,而且需要在英文datasheet中搜寻,总感觉无所下手。所以,分步进行是非常有必要的。今天来实现的功能就是,对这块芯片进行SPI通讯,能读能写。读的话,我们一般都会去读芯片id,看读出的结果是否与datasheet中介绍的一致,同时也就验证了读函数的正确性。至于写函数呢,好像一般也就没有进行验证。我们可以这样,先去读芯片id,保证读函数的正确;再往一个能读写的寄存器中进行写操作,然后再读回来,看写进去的值是否与读出来的值一致,这样也就验证了写函数是否正确。
1)下面简要介绍一下SPI时序。
SSEL:片选信号;
SCK:时钟信号;
MOSI:主机输出,从机输入;
MISO:主机输入,从机输出。

SPI通信时序图1

SPI通信时序图2

SPI通信时序图3

SPI通信时序图4
CPOL:时钟的极性。如果SCK在数据发送之前和发送之后是都高电平,那么CPOL=1;
CPHA:时钟的相位。如果数据的输出在SCK的第一个沿上,那么CPHA=0。
以SPI通信时序图1来进行分析,空闲时刻SCK为高电平,所以CPOL=1;在SCK的第二个沿上(即上升沿),数据是稳定的,可以进行数据采样,所以CPHA=1。
以上4种时序图,做到心里有数就行。
2)以STM32F407为开发平台,通过硬件SPI对W25Q128进行读写操作:
首先我们来看一下W25Q128的引脚定义:



我们采用标准的SPI通信,绘制出如下原理图:


通过W25Q128的datasheet,我们可以知道,W25Q128支持SPI标准通信:模式0(CPOL=0,CPHA=0)与模式3(CPOL=1,CPHA=1)。以下程序我们都采用模式3进行读写操作。

以下为在STM32F407下,SPI1的设置:



读取W25Q128芯片的ID,看其datasheet:

时钟SCK置低电平,发送90h(8个SCK)、00h(8个SCK)、00h(8个SCK)、00h(8个SCK),返回高8位(8个SCK)、低8位(8个SCK)。看图中的DI、DO,就可知道是高位在前,低位在后。所以可以写出读取芯片ID的函数:


因为SPI是全双工通信,在读的时候同时写,所以函数用ReadWrite表示。
那么W25Q128的ID为多少呢,看其datasheet:

所以如果能够正确读取W25Q128的话,W25Q128_ID=0xEF17。
运行以下程序,下载进去之后发现,蜂鸣器响,证明写的程序都是正确的。

如果没有蜂鸣器的话,可以用指示灯,或者通信验证,或者直接进行调试,验证W25Q128_ID是否等于0xEF17。
不知道细心的朋友有没有发现,小秋在读取芯片ID的时候,同时使用了写函数和读函数。使用硬件SPI,读写函数是同一个。SPI1_ReadWriteByte()既是写入函数,又是读取函数,所以现在我们已经验证了读函数和写函数的正确。这样说起来感觉有点拗口,明天小秋写一篇文章,《STM32的SPI通信(2)——模拟SPI》,在读取芯片ID的时候,既有写入函数,又有读取函数,而且这两个函数是不一样的,这样子我们就可以看得比较明显了。此外,今天的单片机平台是基于STM32F407的,明天实现完模拟SPI之后,在STM32F103上实现一下硬件SPI和模拟SPI,看这两个平台有什么不一样的地方。