一、SPI特性
基于三条线的全双工同步传输
● 基于双线的单工同步传输,其中一条可作为双向数据线
● 8 位或16 位传输帧格式选择
● 主模式或从模式操作
● 多主模式功能
● 8 个主模式波特率预分频器(最大值为fPCLK/2)
● 从模式频率(最大值为fPCLK/2)
● 对于主模式和从模式都可实现更快的通信
● 对于主模式和从模式都可通过硬件或软件进行NSS 管理:动态切换主/从操作
● 可编程的时钟极性和相位
● 可编程的数据顺序,最先移位MSB或LSB
● 可触发中断的专用发送和接收标志
● SPI 总线忙状态标志
● SPI TI 模式
● 用于确保可靠通信的硬件CRC 功能:
— 在发送模式下可将CRC 值作为最后一个字节发送
— 根据收到的最后一个字节自动进行CRC 错误校验
● 可触发中断的主模式故障、上溢和CRC 错误标志
● 具有DMA 功能的1字节发送和接收缓冲器:发送和接收请求
二、引脚说明引脚说明
三、器件操作
* 程序配置过程:
①配置相关引脚的复用功能,使能SPIx时钟
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
②初始化SPIx,设置SPIx工作模式
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);
③使能SPIx
void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);
④SPI传输数据
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ;
⑤查看SPI传输状态
SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE);
四、器件操作
SST25VF016B 支持 SPI 总线操作的模式 0(0,0)和模式 3(1,1)。两种模式之间的差异在于当总线主器件处于待机模式并且没有数据传送时的SCK信号状态。SCK信号在模式 0 时为低电平,在模式 3 时为高电平。对于这两种模式,串行数据输入 (SI)在 SCK 时钟信号的上升沿被采样,串行数据输出 (SO)在 SCK 时钟信号的下降沿之后被驱动。
五、写保护引脚(WP#)
写保护 (WP#)引脚用于使能状态寄存器的 BPL 位 (bit 7)的锁定功能。当 WP# 驱动为低电平时,是否执行写状态寄存器 (WRSR)指令由 BPL 位的值 (见表 2)决定。当 WP# 为高电平时, BPL 位的锁定功能被禁止。
六、状态寄存器
写保护 (WP#)引脚用于使能状态寄存器的 BPL 位 (bit 7)的锁定功能。当 WP# 驱动为低电平时,是否执行写状态寄存器 (WRSR)指令由 BPL 位的值 (见表 2)决定。当 WP# 为高电平时, BPL 位的锁定功能被禁止。
6.1 使能写状态寄存器 EWSR、写状态寄存器WRSR
写状态寄存器指令将新值写入状态寄存器的 BP3、BP2、BP1、BP0 和 BPL 位。在输入 WRSR(0x01) 指令的命令序列之前,需先输入EWSR(0x50)使能写状态寄存器
//使能写状态寄存器EWSR、写状态寄存器WRSR
void EWSR(void)
{
SPI_FLASH_L; // SPI_FLASH_L: GPIO_ResetBits(GPIOE,GPIO_Pin_6);
SPI_SendByte(0x50);
SPI_FLASH_H; // SPI_FLASH_H: GPIO_SetBits(GPIOE,GPIO_Pin_6);
SPI_FLASH_L;
SPI_SendByte(0x01);
SPI_SendByte(0);
SPI_FLASH_H;
BUSY();
}
6.2 写使能
写使能 (WREN)指令可将状态寄存器中的写使能锁存器位设置为 “1”,以允许进行写操作。在执行任何写 (编程 / 擦除)操作之前,都必须先执行 WREN 指令。 在执行 WREN 指令之前, CE# 必须驱动为高电平。
//写使能
void WREN(void)
{
SPI_FLASH_L;
SPI_SendByte(0x06);
SPI_FLASH_H;
}
6.3 写禁止
写禁止 (WRDI)指令将写使能锁存器位和 AAI 位复位为 0,以禁止任何新的写操作发生。 WRDI 指令不会终止任何正在进行的编程操作。执行WRDI指令之后,所有正在进行的编程操作都可以持续到TBP。在执行 WRDI 指令之前, CE# 必须驱动为高电平。
void WRDI(void)
{
SPI_FLASH_L;
SPI_SendByte(0x04);
SPI_FLASH_H;
BUSY();
}
6.4 读取状态寄存器(RDSR)
读取状态寄存器(RDSR)指令允许读取状态寄存器。可在任何时间读取状态寄存器,甚至是在写(编程 / 擦除)操作期间。当进行写操作时,应在发送任何新命令前检查 BUSY 位,以确保新命令会被器件正确接收。在输入 RDSR 指令之前,CE# 必须驱动为低电平,并保持低电平直到状态数据被读取为止。读状态寄存器继续使用当前时钟周期,直到被 CE# 上低电平到高电平的转换终止。
u8 RDSR(void)
{
u8 dt;
SPI_FLASH_L;
SPI_SendByte(0x05);
dt=SPI_SendByte(0);
SPI_FLASH_H;
return dt;
}
6.5 忙检测
void BUSY(void)
{
u8 a=1;
while((a&0x01)==1) a=RDSR();
}
七、指令
指令用于读、写 (擦除和编程)和配置 SST25VF016B。指令总线周期是 8 个表示命令 (操作码)、数据和地址的位。在执行任何字节编程、自动地址递增 (AAI)编程、扇区擦除、块擦除、写状态寄存器或全片擦除指令之前,必须先执行写使能 (WREN)指令。表 5 提供了完整的指令列表。所有指令在CE# 从高电平转换到低电平时同步。在 SCK 的上升沿从最高有效位开始接受输入。在输入指令之前,CE# 必须驱动为低电平,而在输入指令的最后一位后,CE# 必须驱动为高电平(读、读 ID 和读状态寄存器指令除外)。在接收到指令总线周期的最后一位之前,CE# 上任何低电平到高电平的转换都将终止正在进行的指令,并将器件恢复为待机模式。指令命令(操作码)、地址和数据都先从最高有效位(MostSignificant Bit, MSb)输入。
7.1 读(25MHZ)
读指令 (03H)支持最高为 25 MHz 的读操作。器件从指定的地址单元开始输出数据。数据输出流连续遍历所有地址,直到被 CE# 上低电平到高电平的转换终止。内部地址指针将自动递增,直到达到最高的存储器地址为止。达到最高的存储器地址后,地址指针将自动递增到地址空间的开始位置 (回绕)。从地址单元 1FFFFFH 读取数据之后,下一次输出将来自地址单元 000000H。通过执行 8 位命令 03H(后面紧跟地址位 [A23-A0])来启动读指令。在读周期的持续时间内,CE# 必须保持有效低电平。
7.2 高速读(80MHZ)
高速读指令支持最高为 80 MHz 的读操作,通过执行 8 位命令 0BH(后面紧跟地址位 [A23-A0] 和一个空字节)来启动。在高速读周期的持续时间内,CE# 必须保持有效低电平。有关高速读序列,请参见图 6。在一个空周期之后,高速读指令从指定的地址单元开始输出数据。数据输出流连续遍历所有地址,直到被 CE# 上低电平到高电平的转换终止。内部地址指针将自动递增,直到达到最高的存储器地址为止。达
到最高的存储器地址后,地址指针将自动递增到地址空间的开始位置 (回绕)。从地址单元 1FFFFFH读取数据之后,下一次输出将来自地址单元 000000H。
//高速读
void Section_Read(u32 addr,u8 *buffer,u16 Size)
{
u16 i=0;
SPI_FLASH_L;
SPI_SendByte(0x0B);
SPI_SendByte((addr&0xFFFFFF)>>16);
SPI_SendByte((addr&0xFFFF)>>8);
SPI_SendByte(addr&0xFF);
SPI_SendByte(0);
while(i
{
buffer[i]=SPI_SendByte(0);
i++;
}
SPI_FLASH_H;
}
7.3、字节编程
字节编程指令用于将所选字节中的位编程为所需数据。当启动编程操作时,所选字节必须处于擦除状态(FFH)。应用到受保护存储器区域的字节编程指令将被忽略。在执行任何写操作之前,必须先执行写使能(WREN)指令。在字节编程指令的持续时间内,CE# 必须保持有效低电平。通过执行 8 位命令 02H (后面紧跟地址位 [A23-A0])来启动字节编程指令。在地址后面,按照从 MSb(bit 7)到 LSb(bit 0)的顺序输入数据。在执行指令之前,CE# 必须驱动为高电平。用户可以轮询软件状态寄存器中的 BUSY 位,或等待 TBP(10µs)时间让内部自定时字节编程操作完成。
7.3.1
自动地址递增(AAI)字编程
AAI 编程指令允许对多个字节的数据进行编程,无需再次发出下一连续地址单元。当要对多个字节或整个存储器阵列编程时,该特性可减少总编程时间。指向受保护存储器区域的 AAI 字编程指令将被忽略。当启动 AAI 字编程操作时,所选地址范围必须处于已擦除状态(FFH)。
当处于 AAI 字编程序列中时,仅下列指令有效:
1 软件写操作结束检测指令
AAI 字(ADH)、写禁止WRDI(04H)、读取状态寄存器RDSR(05H)。
2.硬件写操作结束检测指令
AAI 字(ADH)、写禁止 WRDI(04H)。
7.3.1.1使用软件写操作结束检测时的自动地址递增 (AAI)字编程序列
7.3.1.2使用硬件写操作结束检测时的自动地址递增 (AAI)字编程序列
void Section_Write(u32 addr,u8 *buffer,u16 Size)
{
u16 i=0;
Section_Dell(addr);//先擦除
EWSR();//使能写状态寄存器
WREN();//写使能
SPI_FLASH_L;
SPI_SendByte(0xAD);
SPI_SendByte((addr&0xFFFFFF)>>16);
SPI_SendByte((addr&0xFFFF)>>8);
SPI_SendByte(addr&0xFF);
SPI_SendByte(buffer[0]);
SPI_SendByte(buffer[1]);
SPI_FLASH_H;
i=2;
while(i
{
delay_us(10);
SPI_FLASH_L;
SPI_SendByte(0xAD);
SPI_SendByte(buffer[i++]);
SPI_SendByte(buffer[i++]);
SPI_FLASH_H;
}
delay_us(10);
WRDI();//退出AAI模式
BUSY();
}
7.3.2 4KB
扇区擦除
扇区擦除指令会将所选 4 KB 扇区中的所有位清除为 FFH。应用到受保护存储器区域的扇区擦除指令将被忽略。在执行任何写操作之前,必须先执行写使能 (WREN)指令。在任何命令序列的持续时间内,CE# 都必须保持有效低电平。通过执行 8 位命令 20H (后面紧跟地址位 [A23-A0])来启动扇区擦除指令。在执行指令之前,CE#必须驱动为高电平。用户可以轮询软件状态寄存器中的 BUSY 位,或等待 TSE(25ms)时间让内部自定时扇区擦除周期完成。
void Section_Dell(u32 addr)
{
EWSR();//使能写状态寄存器
WREN();//写使能
SPI_FLASH_L;
SPI_SendByte(0x20);
SPI_SendByte((addr&0xFFFFFF)>>16);
SPI_SendByte((addr&0xFFFF)>>8);
SPI_SendByte(addr&0xFF);
SPI_FLASH_H;
BUSY();//忙检测
}
7.3.3 全片
擦除
全片擦除指令会将器件中的所有位清除为 FFH。如果有任何存储器区域受到保护,全片擦除指令将被忽略。在执行任何写操作之前,必须先执行写使能 (WREN)指令。在全片擦除指令序列的持续时间内,CE# 必须保持有效低电平。通过执行 8 位命令 60H 或 C7H 来启动全片擦除指令。在执行指令之前,CE#必须驱动为高电平。用户可以轮询软件状态寄存器中的 BUSY 位,或等待 TCE(50ms)时间让内部自定时全片擦除周期完成。
void Section_All_dell(void)
{
EWSR();//使能写状态寄存器
WREN();//写使能
SPI_FLASH_L;
SPI_SendByte(0x60);
SPI_FLASH_H;
BUSY();
}
7.3.4 读ID(RDID)
读ID指令(RDID)将器件标识为SST25VF016B,将制造商标识为SST。此命令向后兼容所有SST25xFxxxA器件,当在一个设计中使用多个版本的 SPI 串行闪存器件时,应将此命令用作默认器件标识命令。通过执行 8 位命令 90H 或 ABH(后面紧跟地址位 [A23-A0])来读取器件信息。执行读 ID 指令之后,制造商ID 位于地址 00000H,器件 ID 位于地址 00001H。器件处于读 ID 模式之后,制造商 ID 和器件 ID 输出数据在地址 00000H 和 00001H 之间翻转,直到被 CE# 上低电平到高电平的转换终止。
u16 RDID(void)
{
u16 id=0;
SPI_FLASH_L;
SPI_SendByte(0x90);
SPI_SendByte(0);
SPI_SendByte(0);
SPI_SendByte(0);
id=SPI_SendByte(0);
id=id<<8;
id=id+SPI_SendByte(0);
SPI_FLASH_H;
return id;
}
u8 SPI_SendByte(u8 byte)
{
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)==RESET);
SPI_I2S_SendData(SPI1,byte);
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)==RESET);
return SPI_I2S_ReceiveData(SPI1);
}