SPI 协议简介
3 条总线分别为SCK(时钟)、MOSI(主出从入)、MISO(主入从出),片选线为SS1、2、3(从设备)(多少设备也就这三条ss)
MOSI和MISO能看出是全双工(输入输出两条线,可同时使用)
NSSx下降沿选中当前设备,上升沿结束;
NSS为低时选中,SCK下降沿采样数据;

复用推挽输出

通讯协议(Serial Peripheral Interface),即串行外围设备接口,是一种高速全双工的通信总线。它被广泛地使用在ADC、LCD 等设备与MCU 间,要求通讯速率较高的场合。
可与I2C 章节对比阅读,体会两种通讯总线的差异以及EEPROM 存储器与FLASH存储器的区别。
物理层

SPI 通讯使用3 条总线及片选线,3 条总线分别为SCK、MOSI、MISO,片选线为SS1、2、3
(1) ( Slave Select):从设备选择信号线,常称为片选信号线,也称为NSS、CS,以下用NSS 表示。当有多个SPI 从设备与SPI 主机相连时,设备的其它信号线SCK、MOSI 及MISO 同时并联到相同的SPI 总线上,即无论有多少个从设备,都共同只使用这3 条总线;而每个从设备都有独立的这一条NSS 信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号线。I2C 协议中通过设备地址来寻址、选中总线上的某个设备并与其进行通讯;而SPI 协议中没有设备地址,它使用NSS 信号线来寻址,当主机要选择从设备时,把该从设备的NSS 信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行SPI 通讯。所以SPI 通讯以NSS 线置低电平为开始信号,以NSS 线被拉高作为结束信号。
(2) SCK (Serial Clock):时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,如STM32 的SPI 时钟频率最大为fpclk/2,两个设备之间通讯时,通讯速率受限于低速设备。
(3) MOSI (Master Output,Slave Input):主设备输出/从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机到从机。
(4) MISO(Master Input,,Slave Output):主设备输入/从设备输出引脚。主机从这条信号线读入数据,从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机。

协议层
与I2C 的类似,SPI 协议定义了通讯的起始和停止信号、数据有效性、时钟同步等环节。
这是一个主机的通讯时序。NSS、SCK、MOSI 信号都由主机控制产生,而MISO 的信号由从机产生,主机通过该信号线读取从机的数据。MOSI 与MISO 的信号只在NSS 为低电平的时候才有效,在SCK 的每个时钟周期MOSI 和MISO 传输一位数据。
通讯的起始和停止信号
在图SPI 通讯时序中的标号处,NSS 信号线由高变低,是SPI 通讯的起始信号。NSS 是每个从机各自独占的信号线,当从机在自己的NSS 线检测到起始信号后,就知道自己被主机选中了,开始准备与主机通讯。在图中的标号处,NSS 信号由低变高,是SPI 通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。
数据有效性
SPI 使用MOSI 及MISO 信号线来传输数据,使用SCK 信号线进行数据同步。MOSI 及MISO 数据线在SCK 的每个时钟周期传输一位数据,且数据输入输出是同时进行的。数据传输时,MSB先行或LSB 先行并没有作硬性规定,但要保证两个SPI 通讯设备之间使用同样的协定,一般都会采用图SPI 通讯时序中的MSB (Most Significant Bit)先行模式
观察图中的标号处,MOSI 及MISO 的数据在SCK 的上升沿期间变化输出,在SCK 的下降沿时被采样。即在SCK 的下降沿时刻,MOSI 及MISO 的数据有效,高电平时表示数据“1”,为低电平时表示数据“0”。在其它时刻,数据无效,MOSI 及MISO 为下一次表示数据做准备。
SPI 每次数据传输可以8 位或16 位为单位,每次传输的单位数不受限制。
CPOL/CPHA 及通讯模式
上面讲述的图SPI 通讯时序中的时序只是SPI 中的其中一种通讯模式,SPI 一共有四种通讯模式,它们的主要区别是总线空闲时SCK 的时钟状态以及数据采样时刻。为方便说明,在此引入“时钟极性CPOL”和“时钟相位CPHA”的概念。
时钟极性CPOL 是指SPI 通讯设备处于空闲状态时,SCK 信号线的电平信号(即SPI 通讯开始前、NSS 线为高电平时SCK 的状态)。CPOL=0 时,SCK 在空闲状态时为低电平,CPOL=1 时,则相反。
时钟相位CPHA 是指数据的采样的时刻,当CPHA=0 时,MOSI 或MISO 数据线上的信号将会在SCK 时钟线的“奇数边沿”被采样。当CPHA=1 时,数据线在SCK 的“偶数边沿”采样。

分析这个CPHA=0 的时序图。首先,根据SCK 在空闲状态时的电平,分为两种情况。SCK信号线在空闲状态为低电平时,CPOL=0;空闲状态为高电平时,CPOL=1。
无论CPOL=0 还是=1,因为我们配置的时钟相位CPHA=0,在图中可以看到,采样时刻都是在SCK 的奇数边沿。注意当CPOL=0 的时候,时钟的奇数边沿是上升沿,而CPOL=1 的时候,时钟的奇数边沿是下降沿。所以SPI 的采样时刻不是由上升/下降沿决定的。MOSI 和MISO 数据线的有效信号在SCK 的奇数边沿保持不变,数据信号将在SCK 奇数边沿时被采样,在非采样时刻,MOSI 和MISO 的有效信号才发生切换。

SPI模式 CPOL CPHA 空闲时SCK时钟 采样时刻
0 0 0 低电平 奇数边沿
1 0 1 低电平 偶数边沿
2 1 0 高电平 奇数边沿
3 1 1 高电平 偶数边沿

实际中采用较多的是“模式0”(低电平奇数边沿-上升沿)与“模式3“(高电平偶数边沿-上升沿)

STM32 的SPI 特性及架构

与I2C 外设一样,STM32 芯片也集成了专门用于SPI 协议通讯的外设。

 typedef struct{uint16_t SPI_Direction; /* 设置SPI 的单双向模式*/uint16_t SPI_Mode; /* 设置SPI 的主/从机端模式*/uint16_t SPI_DataSize; /* 设置SPI 的数据帧长度,可选8/16 位*/uint16_t SPI_CPOL; /* 设置时钟极性CPOL,可选高/低电平*/uint16_t SPI_CPHA; /* 设置时钟相位CPHA,可选奇/偶数边沿采样*/uint16_t SPI_NSS; /* 设置NSS 引脚由SPI 硬件控制还是软件控制*/uint16_t SPI_BaudRatePrescaler; /* 设置时钟分频因子,fpclk/分频数=fSCK */uint16_t SPI_FirstBit; /* 设置MSB/LSB 先行*/uint16_t SPI_CRCPolynomial; /* 设置CRC 校验的表达式*/
} SPI_InitTypeDef;

这些结构体成员说明如下,其中括号内的文字是对应参数在STM32 标准库中定义的宏:
(1) SPI_Direction
本成员设置SPI 的通讯方向,可设置为单双线:双线全双工(SPI_Direction_2Lines_FullDuplex),双线只接收(SPI_Direction_2Lines_RxOnly),单线只接收(SPI_Direction_1Line_Rx)、单线只发送模式(SPI_Direction_1Line_Tx)。
(2) SPI_Mode
本成员设置SPI 工作在主从机:主机模式(SPI_Mode_Master) 或从机模式(SPI_Mode_Slave ),这两个模式的最大区别为SPI 的SCK 信号线的时序,SCK 的时序是由通讯中的主机产生的。若被配置为从机模式,STM32 的SPI 外设将接受外来的SCK 信号。
(3) SPI_DataSize
本成员可以选择SPI 通讯的数据帧大小8/16位:是为8 位(SPI_DataSize_8b) 还是16 位(SPI_DataSize_16b)。
(4) SPI_CPOL 和SPI_CPHA
这两个成员配置SPI 的时钟极性CPOL 和时钟相位CPHA,这两个配置影响到SPI 的通讯模式,关于CPOL 和CPHA 的说明参考前面“通讯模式”小节。
时钟极性CPOL 成员,可设置为高电平(SPI_CPOL_High) 或低电平(SPI_CPOL_Low )。时钟相位CPHA 则可以设置为SPI_CPHA_1Edge(在SCK 的奇数边沿采集数据) 或SPI_CPHA_2Edge(在SCK 的偶数边沿采集数据) 。
(5) SPI_NSS
本成员配置NSS 引脚的使用模式, 可以选择为软件还是硬件:硬件模式(SPI_NSS_Hard ) 与软件模式(SPI_NSS_Soft ),在硬件模式中的SPI 片选信号由SPI 硬件自动产生,而软件模式则需要我们亲自把相应的GPIO 端口拉高或置低产生非片选和片选信号。实际中软件模式应用比较多。
(6) SPI_BaudRatePrescaler
本成员设置波特率分频因子,分频后的时钟即为SPI 的SCK 信号线的时钟频率。这个成员参数可设置为fpclk 的2、4、6、8、16、32、64、128、256 分频。
(7) SPI_FirstBit
所有串行的通讯协议都会有MSB 先行(高位数据在前) 还是LSB 先行(低位数据在前) 的问题,而STM32 的SPI 模块可以通过这个结构体成员,对这个特性编程控制。
(8) SPI_CRCPolynomial
这是SPI 的CRC 校验中的多项式,若我们使用CRC 校验时,就使用这个成员的参数(多项式),来计算CRC 的值。

配置完这些结构体成员后,我们要调用SPI_Init 函数把这些参数写入到寄存器中,实现SPI 的初始化,然后调用SPI_Cmd 来使能SPI 外设。

本实验板中的FLASH 芯片(型号:W25Q64) 是一种使用SPI 通讯协议的NOR FLASH 存储器,它的CS/CLK/DIO/DO 引脚分别连接到了STM32 对应的SPI 引脚NSS/SCK/MOSI/MISO 上,其中STM32 的NSS 引脚是一个普通的GPIO,不是SPI 的专用NSS 引脚,所以程序中我们要使用软件控制的方式。
FLASH 芯片中还有WP 和HOLD 引脚。WP 引脚可控制写保护功能,当该引脚为低电平时,禁止写入数据。我们直接接电源,不使用写保护功能。HOLD 引脚可用于暂停通讯,该引脚为低电平时,通讯暂停,数据输出引脚输出高阻抗状态,时钟和数据输入引脚无效。我们直接接电源,不使用通讯暂停功能。
关于FLASH 芯片的更多信息,可参考其数据手册《W25Q64》来了解。若您使用的实验板FLASH的型号或控制引脚不一样,只需根据我们的工程修改即可,程序的控制原理相同。
编程要点
(1) 初始化通讯使用的目标引脚及端口时钟
(2) 使能SPI 外设的时钟
(3) 配置SPI 外设的模式、地址、速率等参数并使能SPI 外设;
(4) 编写基本SPI 按字节收发的函数;
(5) 编写对FLASH 擦除及读写操作的的函数;
(6) 编写测试程序,对读写数据进行校验

使用SPI 发送和接收一个字节的数据

u8 SPI_FLASH_SendByte(u8 byte)函数

SPI_FLASH_SendByte 函数实现了前面讲解的“SPI 通讯过程”:
(1) 本函数中不包含SPI 起始和停止信号,只是收发的主要过程,所以在调用本函数前后要做好起始和停止信号的操作;
(2) 对SPITimeout 变量赋值为宏SPIT_FLAG_TIMEOUT。这个SPITimeout 变量在下面的while 循环中每次循环减1,该循环通过调用库函数SPI_I2S_GetFlagStatus 检测事件,若检测到事件,则进入通讯的下一阶段,若未检测到事件则停留在此处一直检测,当检测SPIT_FLAG_TIMEOUT次都还没等待到事件则认为通讯失败,调用的SPI_TIMEOUT_UserCallback 输出调试信息,并退出通讯;
(3) 通过检测TXE 标志,获取发送缓冲区的状态,若发送缓冲区为空,则表示可能存在的上一个数据已经发送完毕;
(4) 等待至发送缓冲区为空后,调用库函数SPI_I2S_SendData 把要发送的数据“byte”写入到SPI的数据寄存器DR,写入SPI 数据寄存器的数据会存储到发送缓冲区,由SPI 外设发送出去;
(5) 写入完毕后等待RXNE 事件,即接收缓冲区非空事件。由于SPI 双线全双工模式下MOSI 与MISO 数据传输是同步的(请对比“SPI 通讯过程”阅读),当接收缓冲区非空时,表示上面的数据发送完毕,且接收缓冲区也收到新的数据;
(6) 等待至接收缓冲区非空时,通过调用库函数SPI_I2S_ReceiveData 读取SPI 的数据寄存器DR,就可以获取接收缓冲区中的新数据了。代码中使用关键字“return”把接收到的这个数据作为SPI_FLASH_SendByte 函数的返回值,所以我们可以看到在下面定义的SPI 接收数据函数SPI_FLASH_ReadByte,它只是简单地调用了SPI_FLASH_SendByte 函数发送数据“Dummy_Byte”,
然后获取其返回值(因为不关注发送的数据,所以此时的输入参数“Dummy_Byte”可以为任意值)。可以这样做的原因是SPI 的接收过程和发送过程实质是一样的,收发同步进行,关键在于我们的上层应用中,关注的是发送还是接收的数据。
控制FLASH 的指令搞定SPI 的基本收发单元后,还需要了解如何对FLASH 芯片进行读写。FLASH 芯片自定义了很多指令,我们通过控制STM32 利用SPI 总线向FLASH 芯片发送指令,FLASH 芯片收到后就会执行相应的操作。
而这些指令,对主机端(STM32) 来说,只是它遵守最基本的SPI 通讯协议发送出的数据,但在设备端(FLASH 芯片) 把这些数据解释成不同的意义,所以才成为指令。
FALSH
首先:读取 FLASH 芯片 ID根据“JEDEC”指令的时序。常见的是主机端通过读取设备 ID 来测试硬件是否连接正常,或用于识别设备。
由于 FLASH 存储器的特性决定了它只能把原来为“1”的数据位改写成“0”。擦除是把所有的位置一。FLASH 芯片支持“扇区擦除”、“块擦除”以及“整片擦除”

定义FLASH 指令编码表
读取FLASH 芯片ID
FLASH 写使能以及读取当前状态
FLASH 扇区擦除
FLASH 的页写入

void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)函数
不定量数据写入
SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)函数
从FLASH 读取数据
FLASH 芯片的数据读取要简单得多,使用读取指令“Read Data”即可。
SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)函数

/**
* @brief 读取FLASH 数据
* @param pBuffer,存储读出数据的指针
* @param ReadAddr,读取地址
* @param NumByteToRead,读取数据长度
* @retval 无
*/
void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)
{/* 选择FLASH: CS 低电平*/SPI_FLASH_CS_LOW();/* 发送读指令*/SPI_FLASH_SendByte(W25X_ReadData);/* 发送读地址高位*/SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);/* 发送读地址中位*/SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);/* 发送读地址低位*/SPI_FLASH_SendByte(ReadAddr & 0xFF);/* 读取数据*/while (NumByteToRead--){/* 读取一个字节*/*pBuffer = SPI_FLASH_SendByte(Dummy_Byte);/* 指向下一个字节缓冲区*/pBuffer++;}/* 停止信号FLASH: CS 高电平*/SPI_FLASH_CS_HIGH();}

串行FLASH 文件系统FatFs

FatFs 是面向小型嵌入式系统的一种通用的 FAT 文件系统。FatFs 支持FAT12、FAT16、FAT32 等格式

25章 SPI—读写串行FLASH相关推荐

  1. 第24章 SPI—读写串行FLASH—零死角玩转STM32-F429系列

    第24章     SPI-读写串行FLASH 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/ ...

  2. SPI—读写串行FLASH(时序中的无关项)

    SPI-读写串行FLASH 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege 本章 ...

  3. SPI—读写串行FLASH

    1 SPI协议简介 SPI协议是由摩托罗拉公司提出的通讯协议(SerialPeripheralInterface),即串行外围设备接口,是一种高速全双工的通信总线.它被广泛地使用在ADC.LCD等设备 ...

  4. 下载的内容:MSP430F149利用硬件SPI口读写串行Flash M25P64

    /*****************<SPI_Flash.C>**************************************/ #include "SPI_Flas ...

  5. STM32CUBEIDE之SPI读写FLASH进阶串行FLASH文件系统FatFs

    预备知识 >>W25Q128是16M spi flash,一共有256个block ,每个Block 64KB. >>一个Block可以分割为16个扇区(small secto ...

  6. 3.3 SPI串行Flash配置模式

     SPI串行Flash配置模式 1.SPI串行配置介绍 串行Flash的特点是占用管脚比较少,作为系统的数据存贮非常合适,一般都是采用串行外设接口(SPI 总线接口).Flash 存贮器与EEPROM ...

  7. SPI应用——W25Q128串行FLASH

    一.FLASH存储器介绍 FLASH存储器又称闪存,它与EEPROM都是掉电后数据不丢失的存储器,但FLASH存储器容量普遍大于EEPROM,现在基本取代了它的地位.在存储控制上,最主要的区别是FLA ...

  8. STM32 串行FLASH文件系统FatFs

    目录 一.Windows系统为例 二.文件系统的结构与特性 为什么要应用文件分配表? 三.FatFs文件系统 1- FatFs 文件系统源码介绍 2- FatFs在程序中的关系网 四.配置FatFs移 ...

  9. JEDEC标准(JESD216)S FDP对串行Flash在系统中的应用

    摘要:JEDEC标准(JESD216)Serial Flash Discoverable Parameter (SFDP)[1]是在串行Flash中建立一个可供查询的描述串行Flash功能的参数表.文 ...

最新文章

  1. MM32F3277 MicroPython的 mem 函数对于MCU内存访问
  2. boost::graph_property_iter_range用法的测试程序
  3. MySQL 常用语法 之 DISTINCT
  4. 二维数组vector的代码使用简单例子_vector容器_ 用构造函数vectorint()
  5. python坐标轴拉伸_python-Matplotlib垂直拉伸histogram2d
  6. 《信号与系统》解读 前言:经典教材的选择
  7. 三线表里加小短线_三线表的格式
  8. 个人推荐一款并发测试工具
  9. 麒麟操作系统安装/卸载微信
  10. 【读万卷书】《挪威的森林》
  11. java求数组的平均值_Java中数组求平均值,最大值,最小值。
  12. ConcurrentLinkedQueue 源码解析(JDK8)
  13. (随笔备忘)装系统—— centos7下制作U盘启动盘还是失败待试,换大白菜
  14. 笔记 黑马程序员C++教程从0到1入门编程——提高编程02
  15. Viso跨职能流程图连接点操作
  16. 没有技术含量,但能每天赚50刀的GGAD赚钱办法分享
  17. 《我是一只IT小小鸟》推荐序
  18. swiper轮播图切换指示点改变背景颜色
  19. LabVIEW与MATLAB联合编程之使用dll库文件连接
  20. 【附源码】计算机毕业设计SSM天气预报系统

热门文章

  1. 打造超完美主页?20个主页设计技能你不容错过
  2. Python 开发代码片段笔记
  3. JSP及MVC三层架构
  4. 软考和 PMP 哪个含金量更高点?
  5. 在虚拟机(centos)配置postgresql数据库(1) - 安装篇
  6. cropper左右移动_cropper封装的头像裁剪尺寸插件
  7. 程序人生——Hello P2P
  8. 高频交易配对交易学习——Copulas函数理解
  9. poi导出excel详解
  10. 关于twitter爬虫的总结