SPI (Serial Peripheral Interface)串行外设接口是由摩托罗拉公司定义的一种串行外围设备接口,是一种全双工、同步的通信总线,只需要四根信号线即可,节约引脚,同时有利于PCB的布局。正是因为SPI具有这种简单易用的特性,越来越的芯片集成了SPI通信协议,如FLASH、AD转换器等。
特点: 高速、全双工、同步。
模式: SPI分为主、从两种模式。一个SPI通讯系统必须包含一个且只能是一个主设备(Master),外带一个或多个从设备(Slave)。提供时钟的为主设备,接收时钟的设备为从设备,SPI接口的读写操作都是由主设备发起。当存在多个从设备时,通过各自的片选信号进行管理。
SPI一共使用4条信号线进行通讯:
使用SPI协议的主从机设备都有一个串行移位寄存器,主机通过向它的SPI串行寄存器写入一个字节发起一次传输。
SPI只有主模式和从模式之分,没有读和写的说法,外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。【收发同步】
CPOL: 时钟极性。取0表空闲时间低电平,第一个时钟沿为上升沿,取1表示空闲期间高电平,第一个时钟沿是下降沿。
CPHA: 时钟相位。取0表从第一个时钟跳边沿开始检测,取1则从第二个跳边沿开始检测。
然后将极性与相位排列组合,得到以下四个工作模式:
模式 | CPOL【极性】 | CPHA【相位】 | 描述 |
---|---|---|---|
0 | 0 | 0 | 空闲时为低电平,时钟前沿采样【上升沿或第一个跳变沿】 |
1 | 0 | 1 | 空闲时为低电平,时钟后沿采样【下降沿或第二个跳变沿】 |
2 | 1 | 0 | 空闲时为高电平,时钟前沿采样【下降沿或第一个跳变沿】 |
3 | 1 | 1 | 空闲时为高电平,时钟后沿采样【上升沿或第二个跳变沿】 |
优点
缺点
什么是FLASH?
一种存储芯片,通过特定的程序可以修改里面的数据;
FLASH在电子及半导体领域往往表示Flash Memory的意思,即平时所说的“闪存”,全名叫Flash E2PROM Memory;
FLASH存储器又名闪存,它结合了ROM和RAM的长处,不仅具备电子可擦除编程E2PROM的性能,还具有可以快速读取存储数据(NVRAM)的优势,使数据不会因为断电而丢失。
C: 串行时钟。在串行时钟的下降沿,输入的数据data被串行地移出;为接口提供时序;
D: 串行数据输入。输入指令、地址、数据,在串行时钟的上升沿被锁存;
Q: 串行数据输出;指令、地址或数据在时钟上升沿被锁存,在时钟下降沿之后,输出数据Q改变;
S_N: 片选。片选信号S为高时,flash未被选中,数据输出端Q为高阻态,除非内部程序擦除或写状态寄存器时,其处于待机模式,片选信号S为低时,flash处于激活状态;
W_N: 写保护信号。主要目的是冻结受程序或删除指令保护的内存区域;
HOLD_N: 保持信号。用于在片选信号S为低时,取消flash与其他任何设备之间的通信。
容量: 16Mbit/2MByte;一共32个扇区,每个扇区256页,每页256Byte;
页编程速度: 256Bytes/1.4ms;(约为1.462bit/ns)
擦除: 支持512Kbit单扇区擦除以及2MByte整块擦除;
工作模式: 支持00和11两种模式;
该存储芯片的命令有很多,我们这里只提取出来对本次工程有用的命令。
核心命令:
Instruction | Code(H) | Code(B) | 说明 |
---|---|---|---|
WREN | 06 | 0000 0110 | 写使能 |
RDID | 9F | 1001 1111 | 读ID |
RDSR | 05 | 0000 0101 | 读寄存器判断最后一位是0 |
READ | 03 | 0000 0011 | 读数据 |
PP | 02 | 0000 0010 | 页编程 |
SE | D8 | 1101 1000 | 扇区擦除 |
写使能: 1字节指令
读ID: 1字节指令,3字节的ID数据
写状态寄存器: 写入状态寄存器(WRSR)写入状态寄存器(WRSR)指令允许将新值写入状态寄存器。在接受它之前,必须先执行了写入启用(WREN)指令。在写入启用(WREN)指令被解码并执行后,设备将设置写入启用锁存器(WEL)。
读状态寄存器: 读取状态寄存器(RDSR)指令允许读取状态寄存器。状态寄存器可以在任何时候被读取,即使在一个程序、擦除或写状态寄存器周期正在进行中。当其中一个循环正在进行时,建议在向设备发送新指令之前检查“正在写入”(WIP)位。也可以连续读取状态寄存器,如下面时序图所示。
WEL
:写使能锁存bit,高电平代表写使能激活;
WIP
:写状态bit,此bit拉高代表正在写,相当于一个忙碌信号;
BP0 BP1 BP2
:块保护,拉高时代表指定内存区域不接收PP SE的指令;当且仅当两个块保护命令为0时才能使用批量擦除BE命令;
SRWD
:状态寄存器写入禁用。字面意思,很好理解。
读数据: 1字节指令,三字节地址,1字节数据
**页编程: ** 1字节指令,3字节地址,1字节数据
扇区擦除: 1字节指令,3字节地址
名称 | 持续时间 | 描述 |
---|---|---|
C_TIME | 100ns | 一个操作结束后到下一个操作片需要100ns,比如一个命令到下一个命令 |
PP_TIME | 1.4-5ms | 页编程的所需要的时间5ms |
SE_TIME | 1-3s | 擦除所需要的时间3s |
使用FPGA通过SPI协议从M25P16 FLASH芯片中读取/存储数据,并使用数码管显示读出来的设备ID、写入数据、读出数据。
经过手册我们可以看到三种操作:
读ID操作: 读ID指令0x9F
+ 接收(ID高字节
,ID中字节
,ID低字节
);
读数据操作: 读数据指令0x03
+ (地址高字节
,地址中字节
,地址低字节
)+接收数据字节
;
写操作: 分为以下4种子操作
写使能指令0x06
+ 延时;擦除指令0xD8
+ 地址高字节
+ 地址中字节
+ 地址低字节
+ 延时;读状态寄存器指令0x05
+ 接收状态字
;页编程指令0x02
+ 地址高字节
+ 地址中字节
+ 地址低字节
+ 数据字节
+ 延时;这四个子操作相互之间都有联系:
比如在进行扇区擦除和页编程时,都需要先发送一个写使能命令,在完成写使能或扇区擦除后也可以再发送一次读状态寄存器命令;
然后根据返回的状态字中的WEL
,WIP
来判断操作是否做完或者设备是否忙碌,如果符合条件则可以进行下一步操作(页编程)或者回到初始位;这也是为什么我们把读状态寄存器归类到写操作里面。
这里的读状态寄存器与I2C读取AHT10读状态字来检验校验位有异曲同工之处,如果你要坚持严谨的操作,那么按照规定频繁使用这个操作是没有问题的,但如果只是为了完成单纯的写入操作,只要等待的时间够长,就可以抛开读状态寄存器这一步。
key_debounce: 按键消抖模块;
sig_driver: 数码管驱动模块;
spi_master: spi接口模块,负责将数据以符合spi协议的方式接收或发送;
flash_ctrl(flash_wr,flash_rd): flash读写控制模块,负责将需要发送的命令与数据转交给spi接口模块,并负责处理从spi接口读取的数据。
靓仔靓女,暂时看看这个RTL模块图,框图得等后面和核心代码一起上了。
状态图很简单,一共三个状态三个条件:
**IDLE:**初始装填;
**READY:**准备传输状态,这个时候从ctrl模块接收要发送的数据或命令或操作模式;
**TRANS:**传输状态,这里是双向的;
Idle
到Ready
的条件是接收到req请求信号,这里的请求信号由读写控制模块发出,不代表单一的读或写,收到req信号后两条总线都会开始工作;
Trans
到Idle
的条件为end_byte,同样也是等待一个串并转换或并串转换的完成,并且因为是双总线,发与收各管各的,相对I2C而言轻松不少。
接口模块还有一个done
信号,负责传递给读写控制模块告诉这边已经完成发送/接收1个字节,这个状态很重要,关系到读写状态机的状态跳转。
根据之前提到的操作步骤得出:根据读数据和读ID两种操作构成两条分支。
CMD即是发送命令,ADDR即是发送3字节地址,DATA则是接收数据,不同的是ReadData接收的数据是1字节,ReadID接收的数据是3字节;
因为这里设置的使用按键控制读写,key0,key1分别控制读id,读数据,所以IDLE的状态跳转就单纯由按键控制;
剩下的状态条状条件基本一致:end_byte。
这里同样存在一个byte计数器,而且因为每个状态发送或接收的byte数量不一样,所以end_byte拉高的条件也是跟随状态改变的。
add_byte的条件则跟据接口模式传递的done信号和非IDLE模式下拉高。
因为done信号代表发送或接收的那一字节已经完成,所以就不用再多说了~
主状态机:
主状态机负责决定什么时候发送什么命令。
根据我分为的严谨模式
和粗暴模式
分为两种:
这是带RDSR
的,即读状态寄存器用于检验是否可执行下一步操作,可以看出来这RDSR什么时候都可以发,发得越多越严谨,但是咱们初学者嘛,如果只是想验证读写功能,可以不用那么严格地要求自己,只要咱们做完一个操作后等得够久,那么这延时一定是满足要求的,所以我们可以简化一下主状态机:
这样就通俗易懂了。一句话概括:先擦除,再页编程,而且擦除和页编程之前都需要写使能来激活写功能。
既然我们把RDSR简化了,这里就要讨论延时得延多久了,首先需要延时最长的操作是SE,最长3s,那咱们等3.1s就行了,所以我们可以吧从状态机的延时操作计数器设置为3.1s即可。
从状态机:
主状态机是负责命令发送的,那么发送命令操作需要的所有步骤都有从状态机来完成。
结合上文中所分析的操作步骤得出这样一个结果:
命令状态是必须有的,有的操作不用发送地址,有的操作也不用发送数据,比如说写使能,这样就很好理解状态为什么会这样跳转了。
状态跳转条件也会相对而言灵活很多,除了延时操作一般都是根据byte计数器
和done
决定,并且根据主状态机可以决定是否跳过当前状态,比如延时操作,虽然所有状态最终都指向它,但是写使能和读状态寄存器是可以根据主状态机的条件直接略过计数器跳回IDLE的。
施工中…(根据状态机的描述很容易就写出来啦)
施工中…
施工中…
1.“+:”
变量[起始地址 +: 数据位宽] <–等价于–> 变量[(起始地址+数据位宽-1):起始地址]
data[0 +: 8] <–等价于–> data[7:0]
data[15 +: 2] <–等价于–> data[16:15]
2.“-:”
变量[结束地址 -: 数据位宽] <–等价于–> 变量[结束地址:(结束地址-数据位宽+1)]
data[7 -: 8] <–等价于–> data[7:0]
施工中…
全双工的SPI协议相比I2C而言简单不少,用途也更加广泛。