目录
一、SPI通信协议
1.1 SPI简介
- Serial Peripheral Interface,由Motorola公司开发的一种通用数据总线
- 同步时序、全双工,而且有四根通信线:SCK(Serial Clock)、MOSI(Master Output Slave Input)、MISO(Master Input Slave Output)、SS(Slave Select)
- 支持一主多从
与IIC的对比:
简单地说,IIC是精打细算、优雅的学霸,主打功能多;SPI是有钱任性的富家子弟(这在通信线的数量上就能看出),主打性能强
- IIC主要是由于上拉电阻结构,驱动高电平能力弱,所以上升沿耗时长,限制了最大通信速度,最快大约3.4M,支持多主多从(任一时刻当然还是一主一从),谁拉低时钟线谁占用设备
- SPI传输更快,至少几十M(最大速度通常取决于芯片商设计,查手册即可)而且比IIC简单,但缺点是硬件开销大、通信线个数多,会造成资源浪费(比如接收数据时不关心发送的,反过来也可能),主机唯一,时钟线也只有主机控制,从机只有被选择权(片选线拉低)
1.2 硬件电路
所有SPI设备的SCK、MOSI、MISO分别连在一起
多从机时,主机需要引出多条SS控制线,分别接到从机的SS引脚
输出引脚MOSI配置为推挽输出(上升、下降沿快),输入引脚MISO配置为上拉输入
MISO从机输出也有多条怎么办?规定SS不为低电平选中时则保持高阻态
MOSI主机输出当然想给谁发给谁发了
1.3移位示意图
这个是SPI数据传输的核心,概括起来就是交换字节 (SwapByte)
可以看到,主机和从机各自有8位的移位寄存器(高位先行),波特率发生器产生上升/下降沿
举个例子,SPI时序选用模式1(下文会提到)时:
当波特率发生器的上升沿到来,主机和从机的移位寄存器都会移出最高的1位到通信线上(即MOSI、MISO),当下降沿来临,通信线上的数据会移入寄存器,这样就完成了1位的交换,循环8次就完成了字节的交换
如果只想发送不想接收,不读即可;若是只想接收,发0x00或0xff即可
1.4 SPI时序
1.4.1 起始时序
1.4.2 终止时序
1.4.3 交换时序
SPI有两位CPOL、CPHA来配置交换时序的模式0~3,下面模式1对应上文的移位示意图
实际上,模式0用到的多一些,和模式1的区别是CPHA=0,SCK第一个边沿移入,第二个边沿移出
如果要第一个边沿就移入,那根据移位示意图可知,总得先移出一位数据到通信线上,所以可以看到时序图上,移出时刻提前了(可以理解成SS下降沿也就是从机被选中时,提前移出数据,后续按照SCK边沿移出移入即可)
1.4.4 指定地址读写
理解了移位示意图,看懂了交换时序,数据通信当然需要地址,SPI秉承怎么简单怎么来
- 第一个字节:发送给从机的指令码,查手册即可
- 第二个字节:指定读写的地址
- 往后的字节:传输的数据
举个例子,指定地址写时,0x02+要写的地址+数据
二、W25Q64
2.1 W25Q64简介
闪存Nor Flash(另一种为Nand Flash),非易失性存储器,掉电不丢失,64表示64Mbits,即8M字节(需要24位地址线),可用于数据存储、字库存储(OLED、LCD)、固件程序存储,时钟频率80MHz,双重SPI(Dual SPI)时160M,四重SPI(Quad SPI)时320M
双重SPI、四重SPI是什么?
上面提到交换时序的时候,MOSI用于发送,MISO用于接收,这是全双工通信。
- 对于双重SPI,那如果我们只发或只收时造成资源浪费怎么办,这时厂商做出了改进,双重SPI就让MOSI、MISO同时发或者收,这样频率不就翻倍了吗
- 那对于四重SPI,在硬件电路上还有两个传输线,把他们拿来征用,就是四倍频率了
实际上这么一来就不是串行通信了,而是并行
2.2 硬件电路
2.3 W25Q64框图
- 首先8M的内存太大不利于管理,进行了划分,先分成若干Block块(64K*128),再划分成若干Sector扇区(4K*16),再往下还可以细分成页page(256*16)
- 「控制逻辑」:芯片内部进行地址锁存、数据读写等操作都由它来自动完成,相当于管理员,左边就是通信线,和主机相连,主机通过SPI协议,把指令和数据发给控制逻辑,它就会自动去操作内部电路
- 「状态寄存器」:可通过它来写使能/写保护/查询是否忙
- 「写控制逻辑」:和外部WP引脚相连,硬件上配合实现写保护
- 「高电压生成器」:配合Flash进行编程,实现掉电不丢失,具体比较复杂
- 「页地址、字节地址锁存计数器」:它俩加起来刚好3字节,可以用来指定地址,页地址锁存通过写保护行解码选择需要操作哪一页,字节地址锁存通过列解码和旁边的256字节页缓存来进行指定地址的读写,锁存计数是用来读写之后地址指针自增,这样就可以实现从指定地址开始,连续读写多个字节
- 「256字节页缓存」:缓存机制,但缓存的数据转到Flash里需要一定的时间,这个时间会置位状态寄存器中的BUSY位
2.4 Flash操作注意事项
和RAM便捷写入(指定写哪就写哪,而且能覆盖写入)不同,Flash的读写有很多要求,尤其是写入,俗称“读共享,写保护”
写入时:
- 写入操作之前,必须先进行写使能
- 改写时,每个数据位只能由1改写成0,不能0改成1
- 写入数据之前必须先擦除,擦除后,所有数据位变为1(这就弥补了上一条的缺陷)
- 擦除时,不能只擦某一个字节,最少得按4K扇区擦除
这就导致如果你只想改写小部分数据,可以先将要改写数据所在的4K扇区读出来,修改完一次性写进去,写之前当然要擦除;
当然还可以优化这个操作,比如把使用频率高的数据备份到RAM,在RAM里就随便修改了,改完写回Flash;
更粗暴的,可以把你的每个字节数据都分到每个扇区保存,当然这非常浪费资源!!!
- 一次性最多写一页数据,超过一页的数据会回到页首覆盖写入(这是由于页缓存大小的限制,那页缓存为什么不用Flash呢,因为Flash跟不上SPI的速度),这还导致,如果要写入一页数据,最好从页首开始写
- 写入操作或者擦除操作结束,芯片会进入忙状态,置位BUSY位,不响应新的读写操作,注意,不仅写不响应,读也不响应!!!
读出时:
- 不用提前使能,也没有读取大小限制
- 读取操作结束后不会进入忙状态,但不能在忙状态时读取
2.5 手册相关内容
1.状态寄存器1
忙状态时,BUSY位置1,忙状态结束,BUSY位清零指示设备准备好了
写使能锁存位WEL,软件主动写使能后,等到写入操作结束后自动会失能
2.INSTRUCTIONS指令集
- 写使能:start时序+0x06+end时序
- 读状态寄存器1(查看忙状态用):start时序+0x05+读取MISO的S7~S0
- 页编程(也就是写操作):start时序+0x02+3B指定写的地址+交换n字节写入数据
- 读数据:start时序+0x03+3B指定读的地址+交换n字节读出数据
- 扇擦除:start时序+0x20+3B指定读的地址(一般为扇区首地址)
- 读ID:start时序+0x9F+交换3字节读出数据(1B厂商ID,2B设备ID)