一、SPI总线

1、SPI简介

SPI,是Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议。

2、SPI特征

● 3线全双工同步传输
● 8或16位传输帧格式选择
● 主或从操作,支持多主模式
● 8个主模式波特率预分频系数(最大为fPCLK/2)
● 主模式和从模式下均可以由软件或硬件进行NSS管理:主/从操作模式的动态改变
● 可编程的时钟极性和相位
● 可编程的数据顺序,MSB在前或LSB在前
● 可触发中断的专用发送和接收标志
● SPI总线忙状态标志
● 支持可靠通信的硬件CRC
● 可触发中断的主模式故障、过载以及CRC错误标志
● 支持DMA功能的1字节发送和接收缓冲器:产生发送和接受请求

3、SPI工作模式

SPI通信中可作为从机也可以作为主机,这取决于硬件设计和软件设置。
当器件作为主机时,使用一个IO引脚拉低相应从机的选择引脚(NSS),传输的起始由主机发送数据来启动,时钟(SCK)信号由主机产生。通过MOSI发送数据,同时通过MISO引脚接收从机发出的数据。
 当器件作为从机时,传输在从机选择引脚(NSS)被主机拉低后开始,接收主机输出的时钟信号,在读取主机数据的同时通过MISO引脚输出数据。

4、SPI框图

5、通常SPI通过4个引脚与外部器件相连:
● MISO:主设备输入/从设备输出引脚。该引脚在从模式下发送数据,在主模式下接收数据。
● MOSI:主设备输出/从设备输入引脚。该引脚在主模式下发送数据,在从模式下接收数据。
● SCK: 串口时钟,作为主设备的输出,从设备的输入。
● NSS: 从设备选择。这是一个可选的引脚,用来选择主/从设备。它的功能是用来作为“片选引脚”,让主设备可以单独地与特定从设备通讯,避免         数据线上的冲突。从设备的NSS引脚可以由主设备的一个标准I/O引脚来驱动。

6、(NSS)输出管理

对于每个SPI的NSS可以输入,也可以输出。所谓输入,就是NSS的电平信号给自己,所谓输出,就是将NSS的电平信号发送出去,给从机。配置为输出,还是不输出,我们可以通过SPI_CR2寄存器的SSOE位。当SSOE=1时,并且SPI处于主模式控制时(MSTR=1),NSS就输出低电平,也就是拉低,因此当其他SPI设备的NSS引脚与它相连,必然接收到低电平,则片选成功,都成为从设备了。

7、 (NSS)输入管理

NSS软件模式: 
 1.SPI主机:
     需要设置SPI_CR1寄存器的SSM=1和SSI=1,SSM=1是为了使能软件管理,NSS有内部和外部引脚。这时候外部引脚留作他用(可以用来作为GPIO驱动从设备的片选信号)。内部NSS引脚电平则通过SPI_CRL寄存器的SSI位来驱动。SSI=1是为了使NSS内电平为高电平。为什么主设备的内部NSS电平要为1呢?
    STM32手册上说,要保持MSTR=1和SPE=1,也就是说要保持主机模式,只有NSS接到高电平信号时,这两位才能保持置‘1’。
2.SPI从机:       NSS引脚在完成字节传输之前必须连接到一个低电平信号。在软件模式下,则需要设置SPI_CR1寄存器的SSM=1(软件管理使能)和SSI=0.
 NSS硬件模式:
    对于主机,我们的NSS可以直接接到高电平.对于从机,NSS接低就可以。

8、单主和单从应用

9、时钟信号的相位和极性

SPI_CR寄存器的CPOL和CPHA位,能够组合成四种可能的时序关系。CPOL(时钟极性)位控制在没有数据传输时时钟的空闲状态电平,此位对主模式和从模式下的设备都有效。如果CPOL被清’0’,SCK引脚在空闲状态保持低电平;如果CPOL被置’1’,SCK引脚在空闲状态保持高电平。如果CPHA(时钟相位)位被置’1’,SCK时钟的第二个边沿(CPOL位为0时就是下降沿,CPOL位为’1’时就是上升沿)进行数据位的采样,数据在第二个时钟边沿被锁存。如果CPHA位被清’0’,SCK时钟的第一边沿(CPOL位为’0’时就是下降沿,CPOL位为’1’时就是上升沿)进行数据位采样,数据在第一个时钟边沿被锁存。
    CPOL时钟极性和CPHA时钟相位的组合选择数据捕捉的时钟边沿。

(1)数据时钟时序图

(2).数据时钟时序图

10、数据帧格式

根据SPI_CR1寄存器中的LSBFIRST位,输出数据位时可以MSB在先也可以LSB在先。根据SPI_CR1寄存器的DFF位,每个数据帧可以是8位或是16位。所选择的数据帧格式对发送和/或接收都有效。

11、SPI主模式工作原理

配置SPI主模式的步骤如下:
设置SPI_CR1寄存器的BR[2:0]位,来定义串行时钟波特率。
选择CPOL和CPHA位,定义数据传输和串行时钟间的相位关系。
设置DFF位来定义8或16位数据帧格式。
配置SPI_CR1寄存器的LSBFIRST位定义帧格式。
如果NSS引脚需要工作在输入模式,硬件模式中在整个数据帧传输期间应把NSS引脚连接到高电平;在软件模式中,需设置SPI_CR1寄存器的SSM=1和SSI=1。如果NSS引脚工作在输出模式,则只需设置SSOE=1位。
设置MSTR=1和SPE=1,只当NSS引脚被连到高电平,这些位才能保持置位。

12、SPI从模式工作原理

配置SPI从模式的步骤如下:
设置DFF位以定义数据帧格式为8位或16位。
定义数据传输和串行时钟之间的相位关系。
帧格式必须和主设备相同,MSB在前还是LSB在前取决于SPI_CR1寄存器中的LSBFIRST位。
硬件模式下,在完整的数据帧(8位或16位)发送过程中,NSS引脚必须为低电平。软件模式下,设置SPI_CR1寄存器中的SSM=1,SSI=0。
MSTR=0位,设置SPE=1,使相应引脚工作于SPI模式下。

13、状态标志

应用程序通过3个状态标志可以完全监控SPI总线的状态。
1.发送缓冲器空闲标志(TXE)
    此标志为’1’时表明发送缓冲器为空,可以写下一个待发送的数据进入缓冲器中。当写入SPI_DR时,TXE标志被清除。
2.接收缓冲器非空(RXNE)
    此标志为’1’时表明在接收缓冲器中包含有效的接收数据。读SPI数据寄存器可以清除此标志。
3.忙(Busy)标志
    BSY标志由硬件设置与清除(写入此位无效果),此标志表明SPI通信层的状态。

14、SPI中断

二、SST25VF016B工作原理

1、特性

2、框图

3、引脚说明

5、器件操作

SST25VF016B 支持 SPI 总线操作的模式 0(0,0)和模式 3(1,1)。两种模式之间的差异在于当总线主器件处于待机模式并且没有数据传送时的SCK信号状态。SCK信号在模式 0 时为低电平,在模式 3 时为高电平。对于这两种模式,串行数据输入 (SI)在 SCK 时钟信号的上升沿被采样,串行数据输出 (SO)在 SCK 时钟信号的下降沿之后被驱动。

6、写保护引脚 (WP#)

写保护 (WP#)引脚用于使能状态寄存器的 BPL 位 (bit 7)的锁定功能。当 WP# 驱动为低电平时,是否执行写状态寄存器 (WRSR)指令由 BPL 位的值 (见表 2)决定。当 WP# 为高电平时, BPL 位的锁定功能被禁止。

7、状态寄存器

软件状态寄存器提供有关闪存存储器阵列是否可进行任何读或写操作的状态、器件是否写使能以及存储器写保护的状态。在内部擦除或编程操作期间,可对状态寄存器只进行读操作,以确定正在进行的操作的完成状态。

8、写使能锁存器 (WEL)

写使能锁存器 (Write-Enable Latch, WEL)位表示内部寄存器的写使能锁存器状态。如果写使能锁存器位置 “1”,则表示器件写使能。如果该位置 “0”(复位),则表示器件未写使能,并且不接受任何存储器写 (编程 / 擦除)命令。在下列条件下,写使能锁存器位自动复位:

•上电
•写禁止 (WRDI)指令完成
•字节编程指令完成
•自动地址递增 (AAI)编程完成或达到其最高的不受保护的存储器地址
•扇区擦除指令完成
•块擦除指令完成
•全片擦除指令完成
•写状态寄存器指令

9、块保护 (BP3、 BP2、 BP1 和 BP0)

块保护(BP3、BP2、BP1 和 BP0)位定义了要通过软件保护方式防止任何存储器写(编程或擦除)操作的存储器区域的大小,如表 4 中定义。只要 WP# 为高电平或块保护锁定(Block-Protect-Lock,BPL)位为 0,就可以使用写状态寄存器(WRSR)指令编程 BP3、 BP2、 BP1 和 BP0 位。仅当块保护位全部为 0 时,才能执行全片擦除。上电后, BP3、 BP2、 BP1 和 BP0 置为 1。

10、块保护锁定 (BPL)

WP# 引脚驱动为低电平 (VIL)时,将使能块保护锁定 (BPL)位。当 BPL 置 1 时,将阻止对 BPL、BP3、BP2、BP1 和 BP0 位的任何进一步修改。当 WP# 引脚驱动为高电平(VIH) 时,BPL 位没有任何作用,其值为 “无关”。上电后, BPL 位复位为 0。

11、指令

指令用于读、写 (擦除和编程)和配置 SST25VF016B。指令总线周期是 8 个表示命令 (操作码)、数据和地址的位。在执行任何字节编程、自动地址递增 (AAI)编程、扇区擦除、块擦除、写状态寄存器或全片擦除指令之前,必须先执行写使能 (WREN)指令。表 5 提供了完整的指令列表。所有指令在CE# 从高电平转换到低电平时同步。在 SCK 的上升沿从最高有效位开始接受输入。在输入指令之前,CE# 必须驱动为低电平,而在输入指令的最后一位后,CE# 必须驱动为高电平(读、读 ID 和读状态寄存器指令除外)。在接收到指令总线周期的最后一位之前,CE# 上任何低电平到高电平的转换都将终止正在进行的指令,并将器件恢复为待机模式。指令命令(操作码)、地址和数据都先从最高有效位(MostSignificant Bit, MSb)输入。

12、读 (25 MHz)

读指令 (03H)支持最高为 25 MHz 的读操作。器件从指定的地址单元开始输出数据。数据输出流连续遍历所有地址,直到被 CE# 上低电平到高电平的转换终止。内部地址指针将自动递增,直到达到最高的存储器地址为止。达到最高的存储器地址后,地址指针将自动递增到地址空间的开始位置 (回绕)。从地址单元 1FFFFFH 读取数据之后,下一次输出将来自地址单元 000000H。通过执行 8 位命令 03H(后面紧跟地址位 [A23-A0])来启动读指令。在读周期的持续时间内,CE# 必须保持有效低电平。

13、高速读 (80 MHz)

高速读指令支持最高为 80 MHz 的读操作,通过执行 8 位命令 0BH(后面紧跟地址位 [A23-A0] 和一个空字节)来启动。在高速读周期的持续时间内,CE# 必须保持有效低电平。有关高速读序列,请参见图 6。在一个空周期之后,高速读指令从指定的地址单元开始输出数据。数据输出流连续遍历所有地址,直到被 CE# 上低电平到高电平的转换终止。内部地址指针将自动递增,直到达到最高的存储器地址为止。达到最高的存储器地址后,地址指针将自动递增到地址空间的开始位置 (回绕)。从地址单元 1FFFFFH读取数据之后,下一次输出将来自地址单元 000000H。

14、字节编程

字节编程指令用于将所选字节中的位编程为所需数据。当启动编程操作时,所选字节必须处于擦除状态(FFH)。应用到受保护存储器区域的字节编程指令将被忽略。在执行任何写操作之前,必须先执行写使能(WREN)指令。在字节编程指令的持续时间内,CE# 必须保持有效低电平。通过执行 8 位命令 02H (后面紧跟地址位 [A23-A0])来启动字节编程指令。在地址后面,按照从 MSb(bit 7)到 LSb(bit 0)的顺序输入数据。在执行指令之前,CE# 必须驱动为高电平。用户可以轮询软件状态寄存器中的 BUSY 位,或等待 TBP(10µs)时间让内部自定时字节编程操作完成。

15、自动地址递增 (AAI)字编程

AAI 编程指令允许对多个字节的数据进行编程,无需再次发出下一连续地址单元。当要对多个字节或整个存储器阵列编程时,该特性可减少总编程时间。指向受保护存储器区域的 AAI 字编程指令将被忽略。当启动 AAI 字编程操作时,所选地址范围必须处于已擦除状态(FFH)。

当处于 AAI 字编程序列中时,仅下列指令有效: 1 软件写操作结束检测指令 AAI 字(ADH)、写禁止WRDI(04H)、读取状态寄存器RDSR(05H)。 2.硬件写操作结束检测指令 AAI 字(ADH)、写禁止 WRDI(04H)。

在执行任何写操作之前,必须先执行写使能(WREN)指令。通过执行 8 位命令 ADH(后面紧跟地址位[A23-A0])来启动 AAI 字编程指令。在地址后面,连续输入两个字节的数据,每个字节都从 MSb(Bit 7)到 LSb (Bit 0)。 在执行 AAI 字编程指令之前, CE# 必须驱动为高电平。在输入下一个有效命令之前,请检查 BUSY 状态。一旦器件指示不再忙,就可以对下两个连续地址的数据进行编程,然后对再下两个连续地址的数据进行编程,依此类推。
  当编程最后一个所需字或最高的不受保护存储器地址时,通过硬件或软件(RDSR 指令)方法检查忙状态,以确定编程是否完成。编程完成之后,使用适用的方法终止 AAI。如果器件处于软件写操作结束检测模式,则执行写禁止 (WRDI)指令 04H。如果器件处于 AAI 硬件写操作结束检测模式,则执行写禁止(WRDI)指令 04H,再执行 8 位 DBSY 命令 80H。在达到最高的不受保护的存储器地址之后,AAI编程期间不存在回绕模式。

(1)使用硬件写操作结束检测时的自动地址递增 (AAI)字编程序列

(2)使用软件写操作结束检测时的自动地址递增 (AAI)字编程序列

16、4 KB 扇区擦除

扇区擦除指令会将所选 4 KB 扇区中的所有位清除为 FFH。应用到受保护存储器区域的扇区擦除指令将被忽略。在执行任何写操作之前,必须先执行写使能 (WREN)指令。在任何命令序列的持续时间内,CE# 都必须保持有效低电平。通过执行 8 位命令 20H (后面紧跟地址位 [A23-A0])来启动扇区擦除指令。在执行指令之前,CE#必须驱动为高电平。用户可以轮询软件状态寄存器中的 BUSY 位,或等待 TSE(25ms)时间让内部自定时扇区擦除周期完成。

17、32 KB 和 64 KB 块擦除

17、全片擦除

全片擦除指令会将器件中的所有位清除为 FFH。如果有任何存储器区域受到保护,全片擦除指令将被忽略。在执行任何写操作之前,必须先执行写使能 (WREN)指令。在全片擦除指令序列的持续时间内,CE# 必须保持有效低电平。通过执行 8 位命令 60H 或 C7H 来启动全片擦除指令。在执行指令之前,CE#必须驱动为高电平。用户可以轮询软件状态寄存器中的 BUSY 位,或等待 TCE(50ms)时间让内部自定时全片擦除周期完成。

18、读取状态寄存器 (RDSR)

读取状态寄存器(RDSR)指令允许读取状态寄存器。可在任何时间读取状态寄存器,甚至是在写(编程 / 擦除)操作期间。当进行写操作时,应在发送任何新命令前检查 BUSY 位,以确保新命令会被器件正确接收。在输入 RDSR 指令之前,CE# 必须驱动为低电平,并保持低电平直到状态数据被读取为止。读状态寄存器继续使用当前时钟周期,直到被 CE# 上低电平到高电平的转换终止。

19、使能写状态寄存器 EWSR、写状态寄存器WRSR

写状态寄存器指令将新值写入状态寄存器的 BP3、BP2、BP1、BP0 和 BPL 位。在输入 WRSR(0x01) 指令的命令序列之前,需先输入EWSR(0x50)使能写状态寄存器。

20、写使能 (WREN)

写使能 (WREN)指令可将状态寄存器中的写使能锁存器位设置为 “1”,以允许进行写操作。在执行任何写 (编程 / 擦除)操作之前,都必须先执行 WREN 指令。 在执行 WREN 指令之前, CE# 必须驱动为高电平。

21、写禁止 (WRDI)

写禁止 (WRDI)指令将写使能锁存器位和 AAI 位复位为 0,以禁止任何新的写操作发生。 WRDI 指令不会终止任何正在进行的编程操作。执行WRDI指令之后,所有正在进行的编程操作都可以持续到TBP。在执行 WRDI 指令之前, CE# 必须驱动为高电平。

22、读 ID (RDID)

读ID指令(RDID)将器件标识为SST25VF016B,将制造商标识为SST。此命令向后兼容所有SST25xFxxxA器件,当在一个设计中使用多个版本的 SPI 串行闪存器件时,应将此命令用作默认器件标识命令。通过执行 8 位命令 90H 或 ABH(后面紧跟地址位 [A23-A0])来读取器件信息。执行读 ID 指令之后,制造商ID 位于地址 00000H,器件 ID 位于地址 00001H。器件处于读 ID 模式之后,制造商 ID 和器件 ID 输出数据在地址 00000H 和 00001H 之间翻转,直到被 CE# 上低电平到高电平的转换终止。

23、产品订购信息

三、例程:用SPI读取 SST25VF016Bflash

1、例程代码

  1 /*************************************************************************************************************************************
  2 *
  3 *  文件名称:main.c
  4 *  文件功能:主函数文件
  5 *
  6 ***************************************************************************************************************************************/
  7
  8 #include "pbdata.h"//调用自定义公共函数库
  9
 10
 11 /*********************************************************************************
 12 *
 13 * 初始化操作
 14 *
 15 *********************************************************************************/
 16 void RCC_Configuration(void);//系统时钟初始化函数声明
 17 void GPIO_Configuration(void);//GPIO初始化函数声明
 18 void NVIC_Configuration(void);//中断优先级配置函数声明
 19 void USART_Configuration(void);//串口配置函数声明
 20
 21
 22
 23
 24 /********************************************************************************
 25 *
 26 *  函数名称:main(void)
 27 *  函数功能:主函数
 28 *  函数说明:不能用void定义主函数
 29 *
 30 ********************************************************************************/
 31 int main(void)
 32 {
 33
 34     u16 id=0;  //ID
 35     u16 TxSize1=0;  //发送字符的大小
 36     u16 i=0; //用于循环
 37
 38     u8 TxBuffer1[]="liubo tobebest";  //发送的字符
 39     TxSize1=sizeof(TxBuffer1)/sizeof(TxBuffer1[0]);  //获取发送的字符的大小
 40
 41     RCC_Configuration();    //系统时钟高初始化
 42     GPIO_Configuration(); //GPIO初始化
 43     USART_Configuration();//串口初始化
 44     NVIC_Configuration();//串口优先级初始化
 45     SPI_Configuration();//SPI初始化
 46
 47     id=RDID();//获取ID
 48     printf("%X\r\n",id);//串口打印
 49
 50     /*把TxBuffer1数组中的数放入TxBuffer2数组中*/
 51     for(i=0;i<TxSize1;i++)
 52     {
 53         TxBuffer2[i]=TxBuffer1[i];
 54     }
 55
 56
 57     Section_Write(0,TxBuffer2,4096);//写入Flash
 58     memset(TxBuffer2,0,sizeof(TxBuffer2));//把TXBuffer2中前n个字节清0
 59
 60     delay_ms(100);//延时等待
 61     Section_Read(0,TxBuffer2,4096);//ׁ读出Flash到TxBuffer2
 62     printf("%s\r\n",TxBuffer2);//串口打印出读出的数
 63
 64     while(1)
 65     {
 66         delay_ms(1000);
 67     }
 68 }
 69
 70
 71
 72
 73 /********************************************************************************
 74 *
 75 *  函数名称:RCC_Configuration(void)
 76 *  函数功能:系统时钟高初始化函数
 77 *
 78 ********************************************************************************/
 79     void RCC_Configuration(void)//系统时钟高初始化函数
 80   {
 81
 82     SystemInit();//系统初始化
 83     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//串口对应GPIO时钟使能
 84         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//SPI对应GPIO时钟使能
 85     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//SPI对应GPIO时钟使能
 86     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);//SPI对应GPIO时钟使能
 87         RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//串口时钟使能
 88         RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//引脚复用
 89         RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);//SPI1时钟使能
 90
 91     }
 92
 93
 94
 95 /*******************************************************************************
 96 *
 97 * 函数名称:GPIO_Configuration(void)
 98 * 函数功能:GPIO初始化函数
 99 *
100 ********************************************************************************/
101
102     void GPIO_Configuration(void)//GPIO初始化函数
103   {
104
105
106 /*串口引脚配置*/
107         GPIO_InitTypeDef GPIO_InitStructure;//定义一个GPIO设置的结构体变量
108
109 /*输出引脚配置*/
110         /*结构体变量赋值*/
111       GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;////引脚配置TX
112       GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//配置频率
113         GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//发送要配置成复用推挽输出
114         /*对应的GPIO初始化*/
115       GPIO_Init(GPIOA,&GPIO_InitStructure);
116
117
118 /*输入引脚配置*/
119         /*结构体变量赋值*/
120       GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;////引脚配置RX
121         GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;//接收引脚配置成浮空输入
122         /*对应的GPIO初始化*/
123       GPIO_Init(GPIOA,&GPIO_InitStructure);
124
125
126   }
127
128
129 /****************************************************************************
130 *
131 *  函数名称:NVIC_Configuration(void)
132 *  函数功能:配置中断优先级
133 *
134 ****************************************************************************/
135
136 void NVIC_Configuration(void)
137 {
138   NVIC_InitTypeDef NVIC_InitStructure; //定义一个优先级配置结构体变量
139
140   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);//分组
141
142   NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
143   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//抢断优先级
144   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//响应优先级
145   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能
146
147   NVIC_Init(&NVIC_InitStructure);//初始化
148 }
149
150
151 /*********************************************************************************
152 *
153 *  函数名称:
154 *  函数功能:串口配置函数
155 *
156 *********************************************************************************/
157 void USART_Configuration(void)
158 {
159 /*定义串口配置结构体变量*/
160         USART_InitTypeDef USART_InitStructure;//定义一个串口配置结构体变量
161
162
163 /*结构体变量赋值*/
164     USART_InitStructure.USART_BaudRate = 9600;//波特率9600
165     USART_InitStructure.USART_WordLength = USART_WordLength_8b;//位宽,8位
166     USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位1
167     USART_InitStructure.USART_Parity = USART_Parity_No;//不奇偶校验
168     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//流控禁止
169     USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//发送使能
170
171
172 /*发送串口配置初始化*/
173     USART_Init(USART1, &USART_InitStructure);
174
175
176 /*打开串口接收中断*/
177       USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//当接收到数据时,会产生中断
178
179
180 /*打开串口*/
181     USART_Cmd(USART1,ENABLE);//串口使能,打开
182
183 /*清空中断标志位*/
184     USART_ClearFlag(USART1,USART_FLAG_TC);
185 }
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221     

View Code

  1 /*************************************************************************************************************************************
  2 *
  3 *  文件名称:stm32_spi.c
  4 *  文件功能:SPI相关函数
  5 *
  6 ***************************************************************************************************************************************/
  7
  8
  9
 10 #include "pbdata.h"
 11
 12
 13
 14
 15 /*********************************************************************************
 16 *
 17 * 函数名称:void  SPI_Configuration(void)
 18 * 函数功能:SPI配置函数
 19 *
 20 *********************************************************************************/
 21
 22 void  SPI_Configuration(void)
 23 {
 24
 25 /*定义一个SPI配置结构体变量*/
 26    SPI_InitTypeDef     SPI_InitStructure;
 27
 28 /*定义一个SPI的GPIO配置结构体变量*/
 29    GPIO_InitTypeDef       GPIO_InitStructure;
 30
 31 /*配置GPIO*/
 32   /*GPIO配置结构体变量赋值*/
 33    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;////引脚配置
 34    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//配置频率
 35    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//发送要配置成复用推挽输出
 36    /*对应的GPIO初始化*/
 37    GPIO_Init(GPIOA,&GPIO_InitStructure);
 38
 39
 40 /*以太网片选配置*/
 41    /*以太网GPIO配置结构体变量赋值*/
 42    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_7;//以太网引脚配置
 43    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//配置频率
 44    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//发送配置
 45    /*对应的GPIO初始化*/
 46    GPIO_Init(GPIOB,&GPIO_InitStructure);
 47
 48
 49 /*触摸屏片选配置*/
 50    /*以太网GPIO配置结构体变量赋值*/
 51    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4;//触摸屏引脚配置
 52    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//配置频率
 53    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//发送配置
 54    /*对应的GPIO初始化*/
 55    GPIO_Init(GPIOC,&GPIO_InitStructure);
 56
 57
 58
 59 /*Flash片选配置*/
 60    /*FlashGPIO配置结构体变量赋值*/
 61    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;//flash引脚配置
 62    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//配置频率
 63    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//发送配置
 64    /*对应的GPIO初始化*/
 65    GPIO_Init(GPIOE,&GPIO_InitStructure);
 66
 67
 68
 69 /*SPI1配置*/
 70    /*SPI1配置结构体变量赋值*/
 71    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//双线双向全双工
 72    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//CPU主模式
 73    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//8位发送
 74    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//悬空时为高
 75    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//第二个时钟沿捕获
 76    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//内部管理
 77    SPI_InitStructure.SPI_BaudRatePrescaler =SPI_BaudRatePrescaler_8;//8分频
 78    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//高位开始发送
 79    SPI_InitStructure.SPI_CRCPolynomial = 7;
 80    /*SPI1初始化*/
 81    SPI_Init(SPI1, &SPI_InitStructure);
 82
 83    SPI_Cmd(SPI1,ENABLE); //使能SPI
 84
 85
 86
 87 /*禁止片选*/
 88     GPIO_SetBits(GPIOB,GPIO_Pin_7);//禁止以太网
 89     GPIO_SetBits(GPIOC,GPIO_Pin_4);//禁止触摸屏
 90     GPIO_SetBits(GPIOE,GPIO_Pin_6);//禁止flash
 91
 92 }
 93
 94
 95
 96
 97
 98
 99
100
101 /*********************************************************************************
102 *
103 * 函数名称:u8  SPI_SendByte(u8  byte)
104 * 函数功能:SPI发送数据函数
105 *
106 *********************************************************************************/
107
108 u8  SPI_SendByte(u8  byte)
109 {
110     while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)==RESET);//判断是否忙
111
112     SPI_I2S_SendData(SPI1,byte);//发送
113
114     while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)==RESET);//判断是否忙
115
116     return  SPI_I2S_ReceiveData(SPI1);
117
118 }

View Code

1 #ifndef _STM32_SPI_H
2 #define _STM32_SPI_H
3
4 void SPI_Configuration(void);//SPIÅäÖú¯Êý
5 u8 SPI_SendByte(u8 byte);//SPI·¢ËÍÊý¾Ýº¯Êý
6
7 #endif

View Code

  1 /*************************************************************************************************************************************
  2 *
  3 *  文件名称:SST25V016B.c
  4 *  文件功能:SST25V016BFlash相关函数定义
  5 *
  6 ***************************************************************************************************************************************/
  7
  8 #include "pbdata.h"
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18 /********************************************************************************************
 19 *
 20 * 函数名称:u8  RDSR(void)
 21 * 函数功能:读取状态寄存器的值
 22 *
 23 ********************************************************************************************/
 24 u8  RDSR(void)
 25 {
 26    u8  dt;
 27    SPI_FLASH_L;//平常为高,进入拉低
 28    SPI_SendByte(0x05);//发送05
 29    dt=SPI_SendByte(0);//读取数据
 30    SPI_FLASH_H;//拉高
 31    return dt;
 32 }
 33
 34
 35
 36
 37 /********************************************************************************************
 38 *
 39 * 函数名称:void  EWSR(void)
 40 * 函数功能:使能状态寄存器EWSR
 41 *
 42 ********************************************************************************************/
 43 void  EWSR(void)
 44 {
 45    SPI_FLASH_L;//拉低
 46    SPI_SendByte(0x50);//发送50
 47    SPI_FLASH_H;//拉高
 48    SPI_FLASH_L;//拉低
 49    SPI_SendByte(0x01);//发送01
 50    SPI_SendByte(0);//清0
 51    SPI_FLASH_H;//拉高
 52    BUSY();
 53 }
 54
 55
 56
 57
 58
 59
 60 /********************************************************************************************
 61 *
 62 * 函数名称:void  WREN(void)
 63 * 函数功能:写使能
 64 *
 65 ********************************************************************************************/
 66 void  WREN(void)
 67 {
 68    SPI_FLASH_L;//拉低
 69    SPI_SendByte(0x06);//发送06
 70    SPI_FLASH_H;//拉高
 71 }
 72
 73
 74
 75
 76
 77 /********************************************************************************************
 78 *
 79 * 函数名称:void  WRDI(void)
 80 * 函数功能:写禁止
 81 *
 82 ********************************************************************************************/
 83
 84 void  WRDI(void)
 85 {
 86    SPI_FLASH_L;//拉低
 87    SPI_SendByte(0x04);//发送04
 88    SPI_FLASH_H;//拉高
 89    BUSY();
 90 }
 91
 92
 93
 94
 95
 96
 97 /********************************************************************************************
 98 *
 99 * 函数名称:void BUSY(void)
100 * 函数功能:忙检查
101 ********************************************************************************************/
102 void BUSY(void)
103 {
104     u8 a=1;
105     while((a&0x01)==1) a=RDSR();
106 }
107
108
109
110
111
112 /********************************************************************************************
113 *
114 * 函数名称:u16  RDID(void)
115 * 函数功能:读ID
116 ********************************************************************************************/
117 u16  RDID(void)
118 {
119    u16  id=0;
120
121    SPI_FLASH_L;//拉低
122
123    SPI_SendByte(0x90);//发送90
124    SPI_SendByte(0);//发送0
125    SPI_SendByte(0);//发送0
126    SPI_SendByte(0);//发送0
127
128    id=SPI_SendByte(0);//读ID
129    id=id<<8;
130    id=id+SPI_SendByte(0);
131    SPI_FLASH_H;//拉高
132
133    return id;
134 }
135
136
137
138
139
140 /********************************************************************************************
141 *
142 * 函数名称:void  Section_Dell(u32 addr)
143 * 函数功能:扇区擦除
144 ********************************************************************************************/
145 void  Section_Dell(u32 addr)
146 {
147    EWSR();//使能寄存器
148    WREN();//写使能
149
150    SPI_FLASH_L;//拉低
151    SPI_SendByte(0x20);//发送20
152    SPI_SendByte((addr&0xffffff)>>16);//发送地址
153    SPI_SendByte((addr&0xffff)>>8);//发送地址
154    SPI_SendByte(addr&0xff);//发送地址
155    SPI_FLASH_H;//拉高
156    BUSY(); //忙检测
157 }
158
159
160
161
162
163
164 /********************************************************************************************
165 *
166 * 函数名称:void  Section_Read(u32 addr, u8 *buffer,u16 size)
167 * 函数功能:扇区读
168 ********************************************************************************************/
169 void  Section_Read(u32 addr, u8 *buffer,u16 size)
170 {
171    u16 i=0;
172
173    SPI_FLASH_L;//拉低
174    SPI_SendByte(0x0B);//发送0B
175    SPI_SendByte((addr&0xffffff)>>16);//发送地址
176    SPI_SendByte((addr&0xffff)>>8);//发送地址
177    SPI_SendByte(addr&0xff);//发送地址
178    SPI_SendByte(0);//发送0
179
180    while(i<size)
181    {
182       buffer[i]=SPI_SendByte(0);//读数据
183       i++;
184     }
185
186         SPI_FLASH_H;//拉高
187 }
188
189
190
191
192 /********************************************************************************************
193 *
194 * 函数名称:void Section_Write(u32 addr,u8 *buffer,u16 Size)
195 * 函数功能:写扇区
196 ********************************************************************************************/
197 void Section_Write(u32 addr,u8 *buffer,u16 Size)
198 {
199    u16 i=0;
200
201    Section_Dell(addr);//先擦除
202
203    EWSR();//使能写状态寄存器
204    WREN();//写使能
205
206    SPI_FLASH_L;//拉低
207    SPI_SendByte(0xAD);//发送AD
208    SPI_SendByte((addr&0xffffff)>>16);//发送地址
209    SPI_SendByte((addr&0xffff)>>8);//发送地址
210    SPI_SendByte(addr&0xff);//发送地址
211    SPI_SendByte(buffer[0]);//发送buffer[0]
212    SPI_SendByte(buffer[1]);//发送buffer[1]
213    SPI_FLASH_H;//拉高
214
215    i=2;
216    while(i<Size)
217    {
218       delay_us(10);//写之前先延时
219           SPI_FLASH_L;
220       SPI_SendByte(0xAD);//发送0xAD
221       SPI_SendByte(buffer[i++]);//发送buffer[i++]
222       SPI_SendByte(buffer[i++]);//发送buffer[i++]
223       SPI_FLASH_H;//拉高
224    }
225
226    delay_us(10);
227    WRDI();//退出AAI模式
228    BUSY();//忙检测
229
230 }
231
232
233
234
235
236 /********************************************************************************************
237 *
238 * 函数名称:void  Section_ALL_dell(void)
239 * 函数功能:全芯片擦除
240 ********************************************************************************************/
241 void  Section_ALL_dell(void)
242 {
243
244    EWSR();//使能写状态寄存器
245    WREN();//写使能
246
247    SPI_FLASH_L;//拉低
248    SPI_SendByte(0x60);//发送60
249    SPI_FLASH_H;//拉高
250    BUSY();//忙检测
251
252
253 }

View Code

 1 /*************************************************************************************************************************************
 2 *
 3 *  文件名称:SST25V016B.h
 4 *  文件功能:SST25V016BFlash相关函数声明
 5 *
 6 ***************************************************************************************************************************************/
 7
 8
 9
10 #ifndef  _SST25VF016B_H
11 #define  _SST25VF016B_H
12
13 #define  SPI_FLASH_L  GPIO_ResetBits(GPIOE,GPIO_Pin_6);//falsh拉低
14 #define  SPI_FLASH_H  GPIO_SetBits(GPIOE,GPIO_Pin_6);//flash拉高
15
16
17
18
19 u8 RDSR(void);//读取状态寄存器的值
20 void EWSR(void);//使能状态寄存器EWSR
21 void WREN(void);//写使能
22 void WRDI(void);//写禁止
23 void BUSY(void);//忙检查
24 u16 RDID(void);//读ID
25 void Section_Dell(u32 addr);//扇区擦除
26 void Section_Read(u32 addr,u8 *buffer,u16 Size);//读扇区
27 void Section_Write(u32 addr,u8 *buffer,u16 Size);//写扇区
28 void Section_All_dell(void);//全芯片擦除
29
30
31
32
33 #endif

View Code

  1 /****************************************************************************************************************
  2 *
  3 * 文件名称:pbdata.c
  4 * 文件功能:自定义函数或者全局变量的初始化
  5 *
  6 ****************************************************************************************************************/
  7
  8 /*头文件声明*/
  9 #include "pbdata.h"
 10
 11
 12
 13
 14 /********************************************************************************************
 15 *
 16 * 自定义全局变量
 17 *
 18 ********************************************************************************************/
 19 u8 dt=0;
 20
 21 u8 TxBuffer2[4096];//用作读写Flash的缓冲器数组
 22
 23
 24
 25 /******************************************************************************************
 26 *
 27 * 自定义函数
 28 *
 29 ******************************************************************************************/
 30
 31
 32
 33 /**************************************************
 34 *
 35 *  函数名称:delay_us(u32 nus)
 36 *  函数功能:微秒延时函数
 37 *  输入参数:输入值为延时us
 38 *
 39 ***************************************************/
 40 void delay_us(u32 nus)
 41 {
 42     u32 temp;
 43     SysTick->LOAD = 9*nus;//载入初值,72M/8=9M,也就是1/9us,9*1/9us,所以是执行9次
 44     SysTick->VAL=0X00;//清空计数器,清空后,就自动设置自己设定的计数器的值
 45     SysTick->CTRL=0X01;//使能,减到零动作(不发生中断),采用外部时钟
 46
 47     do
 48     {
 49           temp=SysTick->CTRL;//标志位,等到一直减到0
 50          }while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达
 51
 52     SysTick->CTRL=0x00; //关闭计数器
 53     SysTick->VAL =0X00; //清空计数器
 54 }
 55
 56
 57
 58
 59
 60
 61
 62 /***************************************************
 63 *
 64 * 函数名称:delay_ms(u16 nms)
 65 * 函数功能:毫秒级延时
 66 * 输入参数:输入值位延时ms
 67 *
 68 ****************************************************/
 69 void delay_ms(u16 nms)
 70 {
 71     u32 temp;
 72     SysTick->LOAD = 9000*nms;//载入初值,72M/8=9M,也就是1/9us,9*1/9us,所以是执行9000次
 73     SysTick->VAL=0X00;//清空计数器,清空后,就自动设置自己设定的计数器的值
 74     SysTick->CTRL=0X01;//使能,减到零动作(不发生中断),采用外部时钟
 75
 76     do
 77     {
 78           temp=SysTick->CTRL;//标志位,等到一直减到0
 79          }while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达
 80
 81     SysTick->CTRL=0x00; //关闭计数器
 82     SysTick->VAL =0X00; //清空计数器
 83 }
 84
 85
 86
 87 /****************************************************
 88 *
 89 * 重定义printf函数部分
 90 *
 91 ****************************************************/
 92 int fputc(int ch,FILE *F)
 93 {
 94
 95     USART_SendData(USART1,(u8)ch);
 96
 97     while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);//等待发送完成,判断标志位
 98
 99     return ch;
100 }

View Code

 1 /*pbdata.h*/
 2 /***************************************************************************************************
 3 *
 4 * 文件名称:pbdata.h
 5 * 文件功能:自定义的函数和全局变量的声明头文件
 6 *
 7 ***************************************************************************************************/
 8
 9 #ifndef _pbdata_H
10 #define _pbdata_H
11
12
13
14
15
16 /********************************************************************
17 *
18 *  调用的头文件放在这里
19 *
20 ********************************************************************/
21 #include "stm32f10x.h"
22 #include "misc.h"
23 #include "stm32f10x_exti.h"
24 #include "stm32f10x_tim.h"
25 #include "stm32f10x_usart.h"
26 #include "stm32f10x_spi.h"
27 #include "stdio.h"
28 #include <string.h>
29
30 #include "stm32_spi.h"
31 #include "SST25VF016B.h"
32
33
34
35 /********************************************************************
36 *
37 *  自定义全局变量声明
38 *
39 ********************************************************************/
40 extern u8 dt;
41
42 extern u8 TxBuffer2[4096];//读写Flash的缓冲器数组声明
43
44
45
46 /********************************************************************
47 *
48 *  自定义全函数声明
49 *
50 ********************************************************************/
51 void delay(u32 nCount);
52 void delay_us(u32 nus);
53 void delay_ms(u16 nms);
54 int fputc(int ch,FILE *F);
55
56
57
58 #endif

View Code

2、工程下载

http://download.csdn.net/detail/a1181803348/8779957

转载于:https://www.cnblogs.com/STM32bo/p/4557301.html

19、SPI 和 SST25VF016B相关推荐

  1. SPI读写SST25VF016B

    一.SPI特性 基于三条线的全双工同步传输 ● 基于双线的单工同步传输,其中一条可作为双向数据线 ● 8 位或16 位传输帧格式选择 ● 主模式或从模式操作 ● 多主模式功能 ● 8 个主模式波特率预 ...

  2. SPI实现SST25VF016B驱动(Flash)

    本文在STM32F103上实现SSH25的驱动,实现数据的写入和读取: 1:此驱动未加擦除时候是否擦除成功的判断,一般写入FLASH不成功的最大原因就是擦除不成功,所以一般特选FLASH里面的一个地址 ...

  3. spi收发时的寄存器sr不变_「正点原子Linux连载」第二十七章SPI实验(二)

    1)实验平台:正点原子Linux开发板 2)摘自<正点原子I.MX6U嵌入式Linux驱动开发指南> 关注官方微信号公众号,获取更多资料:正点原子 文件bsp_spi.c中有两个函数:sp ...

  4. 【正点原子Linux连载】第二十七章 SPI实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  5. SPI 读取不同长度 寄存器_正点原子Linux第二十七章SPI实验

    1)资料下载:点击资料即可下载 2)对正点原子Linux感兴趣的同学可以加群讨论:935446741 3)关注正点原子公众号,获取最新资料更新 第二十七章SPI实验 同I2C一样,SPI是很常用的通信 ...

  6. 【USCI】UART、IrDA、SPI、I2C MSP430学习经

    2010-06-28 18:40 SPI.IIC.UART区别 第一个区别当然是名字: SPI(Serial Peripheral Interface:串行外设接口); I2C(INTER IC BU ...

  7. STM32外设有哪些?外设在总线上是怎么挂载的?

    一:STM32外设有哪些? 外设在STM32库文件的下面路径下:(STM32库:stm32f10x_stdperiph_lib) stm32f10x_stdperiph_lib\STM32F10x_S ...

  8. yota3墨水屏设置_使用ESP32驱动电子墨水屏

    未经私信同意禁止转载! 一.简介 显示技术发展很快,高中的时候大家还在学习阴极射线管显示,天天在算电子的轨迹.而如今随处可见LED显示液晶显示. 今天给大家介绍一下电子墨水屏.目前生活中比较常见的是在 ...

  9. LuatOS | 轻松DIY简易天气时钟,训练营资料福利全开放

    LuatOS | 轻松DIY简易天气时钟,训练营资料福利全开放 大家好~ 合宙发起的Luat免费训练营第一期,目前已划上小小句号啦!初次开营虽说经验不足,却是大家共同的努力与坚持.期间汇聚了600+热 ...

  10. 基于嵌入式QT界面GPS定位系统设计与实现

    目 录 第1章 绪论- -1 1.1 选题背景和意义- - .1 1.2 主要研究内容- - -1 第2章 GPS系统基础知识介绍- - - -2 2.1 GPS 定位的坐标系统- - - -2 2. ...

最新文章

  1. git branch 为什么会进入编辑状态_gitamp;github(总结git与github的基本用法)
  2. WinAPI: 输入光标相关的函数[3]
  3. Office中如何实现公式颜色的更改?
  4. mongoDB add user in v3.0 问题的解决(Property 'addUser' of object admin is not a func)
  5. Linux基本命令之grep命令常见用法
  6. 安卓-内部存储、外部存储目录
  7. android系统功耗问题:systrace
  8. VC与VS的版本对应关系,VC到底是什么?为啥总提示缺少VC
  9. ccfb类会议有哪些_CCF推荐国际学术会议
  10. 【Code】背包问题九讲(崔添翼)
  11. python用函数判断一个数是否为素数,python分享是否为素数 python输入并判断一个数是否为素数...
  12. 激光打印机无线服务器,插上无线翅膀 兄弟1218W激光打印机评测
  13. matlab 转换为相对湿度
  14. 极米 Z7X参数 极米 Z7X评测
  15. 【案例二】小明都可以买什么
  16. 简单记录下几家公司的面试经历(Java一年经验)
  17. 关于eclipse中js文件没有提示代码的解决
  18. c语言判断一个素数能被几个9整除,判断一个素数能被几个9整除
  19. Python量化交易开源框架:AmazingQuant
  20. Jasperreport+studio设计报表教程(七)--PDF中文显示

热门文章

  1. Zotero——论文管理神器
  2. 【Scratch案例实操】scratch我们爱编程 scratch编程案例教学 scratch创意编程 少儿编程教案
  3. DDA算法和Bresenham算法
  4. 图书管理系统UML课程设计
  5. 运行 Excel 脚本
  6. HTML 制作简单的个人简历
  7. 线性调频信号(Chirp信号)公式
  8. Java周记(第一周)
  9. 计算机网络课程设计实验报告
  10. 笔记本电脑显示打印机服务器关闭,电脑打印机服务能设定自动关闭吗