目录
SPI Flash是使用SPI协议的Flash。
STM32也存在SPI硬件。
一.SPI简介
串行外围设备接口,是高速全双工(同时接受和发送)的通信总线。广泛应用在ADC,LCD等与MCU之间要求通讯速率较高的场合。
二.SPI物理层:
- SS:从设备选择线,又被称为片选信号线,也叫NSS,CS。主机通过片选信号线把某个设备设置为低电平,把该设备选中。SPI通讯以SS线置为低电平为开始信号,SS被拉高作为结束信号。
- MOSI:主机到从机。
- MISO:从机到主机。
- SCK:SPI时钟频率最大为Fpclk/2(挂载总线的时钟频率的二分频)
三.SPI协议层:
SPI协议规定了通讯的起始和停止信号,数据有效性,时钟同步等环节。
- 1.NSS由高变低,选中从机,开始传输
- 2.触发时,发送和接收信号不稳定。
- 3.采样阶段,MOSI和MISO信号稳定,采集数据为低电平
在什么时候触发和采样需要配置CPOL时钟极性和CPHA时钟相位。CPOL:=0,在空闲状态时,SCK为低电平;CPHA:=0,数据线的有效信号在SCK的奇数边沿采样。组合之后,会有四种模式。
在通讯时,通讯的两个设备之间SPI的模式一定要一样,包括CPOL,CPHA,还有数据高位先行还是低位先行。
四.SPI框图和通讯过程
STM32的SPI外设可用作通讯的主机及从机,完全支持SPI的四种模式。还支持双线全双工,双线单向以及单线模式。
STM32103有3个SPI外设,SPI2和SPI3可以作为I2S。SPI1是APB2上的设备,最高速率达36Mbits/s,SPI2和SPI3是APB1上的设备,最大速率18Mbits/s.
用软件控制NSS产生开始和停止信号,使用普通GPIO,设置为推挽输出输入,通过把GPIO端口拉低或者拉高来控制片选或非片选信号。硬件模式下,SPI片选信号由SPI硬件自动产生。
- 时钟控制逻辑:设置分频因子SPI_BR
- 数据控制逻辑:SPI的MOSI和MISO都接到数据移位寄存器上,数据移位寄存器的数据都来自接收缓冲区和发送缓冲区。写操作,通过写数据寄存器把数据填充到发送缓冲区。读操作,通过数据寄存器获得接收缓冲区的内容。SPI_DR
- 整体控制逻辑:负责协调整个SPI外设,配置SPI模式,波特率,主从模式,数据高低先行等等。SPI_CR1,CR2
通讯过程:
通讯开始,SCK时钟开始运行。MOSI把发送缓冲区中的数据一位一位地传输出去;MISO把数据一位一位地存储到数据缓冲区。在发送缓冲区中的数据全部被发送出去后,TXE置1,若要继续发送数据,则往DR中写入数据。在输入(接收缓冲区)满8位时,RXNE置1,从数据寄存器中读出数据。
假如设置了TXE或者RXNE的中断,则它们置一时,会触发SPI中断,进入同一个中断服务函数。进入中断服务程序后,可以通过检查寄存器位来了解是哪一个事件,再分别进入不同的程序执行不同的任务。也可以使用DMA方式来收发DR中的数据。
五.SPI初始化
- Direction:双线全双工SPI_Direction_2Lines_FullDuplex
- Mode:主机/从机模式。SCK由主机产生,从机接收SCK信号
- DataSize:SPI的数据帧长度8/16位。
- CPOL,CPHA
- NSS:软件/硬件模式。常用软件模式。
- BaudRatePrescaler:设置波特率分频因子,常设置为2.
- Firstbit:高/低位先行(高低位先行会影响SPI通讯时,地址高位和低位的传输顺序)
- CRC校验表达式:不用可以随便设置。
把以上的结构体成员配置好,再把结构体写到初始化函数中,实现初始化。使能SPI。
注:CS引脚可以不用引脚图上的分配,可以随便选一个引脚(设置为推挽输出),使用软件来控制高低电平。
其余三个引脚:MISO设置为浮空输入;MOSI和SCK设置为复用推挽输出。
六.FLSH
WP写保护,CS片选信号,CLK时钟,HOLD:hold input。HOLD和WP直接接高电平,不需要使用。
实质就是一个存储器,相对EEPROM,内存较大,传输速度较快。型号:W25Q64,FLASH最高支持80KHZ,STM32最高36KHZ。FLASH支持SPI的Mode0和Mode3。FLASH的ID号可以用来检验STM32是否和FLASH正常连接。
norflash可以一个字节写入。nandflash必须以块或扇区为单位写入或读取。
FLASH的存储特性:
Flash在写入之前要先擦除。擦除时会把数据位全部置1。写入数据时只能把原本为1的数据位改成0。擦除时必须按最小单位(扇区=4096字节,具体多少字节要看手册)来擦除。
FLASH通讯过程:
- STM32怎么读写FLASH?了解FLASH状态寄存器的值?通过SPI接口发送命令代码给FLASH,flash会返回状态的值给STM32(会带括号,括号表示数据的方向)或者STM32写入flash。命令代码看手册。
- FLASH内部时序,读写时间较长,通过读取状态寄存器status register->BUSY来判断是否完成。
- FLASH的写命令和擦除命令之前需要写使能。写完或者擦除完之后需要等待内部时序结束。
- FLASH的命令代码:
- Write Enable:命令代码0x06 写使能
- Sector erase:命令代码0x20 3个字节的地址(相应扇区的最低地址) 擦除一个扇区的数据
- WriteData:命令代码0x02 3个字节的地址+1个字节的数据 写入数据
- DUMMY:命令代码0x00 在发送完读指令和三个字节的地址后,SPI发送DUMMY,FLASH将返回该地址的一个字节的内容。
- READ Status:命令代码0x05 读FLASH状态,FLASH在收到READ Status命令和DUMMY之后返回的值表示写操作是否完成。
- ReadData:命令代码0x03 3个字节地址 读取数据
- Read JEDEC ID:命令代码0x9f 读取FLASH的ID
- Power down:0xB9 低功耗状态
- Release power down:0xAB 释放低功耗状态
七.编程
根据FLASH通讯特点,可知,在配置好SPI_GPIO,初始化GPIO和SPI结构体之后,需要编写SPI-FLASH的写使能,发送-接收(FLASH状态检查),等待内部时序完成,擦除,写入,读取函数。
- 第一步:写SPI的发送-接收函数。等待TX为1,将数据送到发送移位寄存器,发送出去;等待RXNE为1,将接收移位寄存器中的值读取出来。将接收的数据作为返回值返回。这个函数可以用来SPI读取FLASH内部状态寄存器标志位。
- 第二步:写使能函数。将FLASH写使能。
- 第三步:等待FLASH内部时序完成函数。
- 第四步:FLASH擦除函数。
- 第五步:FLASH的读写函数。
- 第六步:FLASH的ID号读取函数。
八.遇到哪些问题?
1.打印ID号是错误的,0xffffff.往FLASH中写入数据之后,把写入的数据读出来,显示的数据全为0,是不正确的数字。
错误代码如下:
修改代码:
程序代码是正确的,但是跑了程序之后仍然显示错误,打印出获得的ID号码是0xffffff,而不是0xef4017。这是为什么呢?
找到解决办法:打印的0Xffffff是FLASH原本的数值。FLASH并没有对SPI的指令作出反应。查看指南者电路原理图,发现SPI1的CS引脚是使用了PC0,不是PA4。还要记得打开GPIOC的时钟。
2.在解决问题1时,不断检查是否是SPI配置有问题,尝试修改代码,但是在调试过程中,使用单步调试,开始直接卡住,无法进行。出现Go to infinite loop when Hard Fault exception occurs (硬件错误)提示,程序进入硬件错误中断。表示可能出现程序访问超出越界,数组溢出,堆栈溢出等错误。问题出在了哪里?
解决办法:发现将两个存储数据的数组放在main函数内程序是无法进行的。必须将数组放在main函数外面。这是为什么,暂时不得而知。