SPI读写W25Q64实现教程:一步步操作指南
目录
前言
一、从0开始讲清SPI通信协议
(1)简介
(2)通信本质
(3)通信时序
1.起始条件
2.终止条件
3.交换字节
4.完整时序
二、W25Q64
(1)简介
(2)引脚功能图
(3)内部空间框图
三、 软件模拟SPI
(1)通信层
1.通信引脚配置
2.SPI起始条件 和 终止条件
3.SPI写入一个字节
(2)硬件驱动层
(3)主函数
总结
前言
本文为作者对SPI通信协议的知识总结以及简单应用,通过软件翻转GPIO电平模拟SPI实现读写W25Q64。
提示:如有错误请尽情指出,希望对广大读者有所帮助。
一、从0开始讲清SPI通信协议
(1)简介
SPI是一种通信速率极高的全双工同步通信协议,它是一主多从的模式。
共有四根及以上根信号线,分别是SCK时钟线、MOSI主机输出从机线(Master Output Slave Input)、MISO主机输入线(Master Input Slave Output)和SS片选信号线。
有多少个从机,则配置多少根SS信号线,空闲状态时,SS信号线默认处于高电平。当需要与某一从机进行通信时,拉低相对应的SS信号线即可。由此可知,SPI通信的起始条件和终止条件分别是拉低SS线和拉高SS线。
SPI的输出引脚(MOSI、SS、SCK)应配置为推挽输出,从而使得高低电平驱动变化十分迅速,达到更高的传输速度。
当有多个从机时,从机输出也有多个,但主机输入只有一个,所以当从机的SS引脚为高电平时,也就是从机未被选中时,它的MISO引脚必须为高阻态,相当于引脚断开,不输出任何电平,这样就可以防止一条线有多个输出,而导致电平冲突的问题了。
(2)通信本质
SPI的通信本质就是主机和从机之间通过移位寄存器交换字节数据。
主机的移位寄存器会将一个字节的8位通过MOSI逐次放在从机的MOSI上。
同理,从机的移位寄存器也会将一个字节的8位通过MISO逐次放在主机的MISO上,由此即可完成数据的交换。
当我们只需要一个设备的数据时,那么我们可以只对一方的数据进行操作,舍弃另一方的数据即可。
(3)通信时序
SPI的通信时序主要由三大部分组成,分别是起始条件、交换字节、终止条件。
1.起始条件
SS从高电平切换到低电平
2.终止条件
SS从低电平切换到高电平
3.交换字节
SPI的交换字节时序共有四种模式,它们之间的区别在于不同电平时期,移入/移出数据。
由于模式0最常用,所以本文只对模式0进行讲解。
在模式0中,空闲状态时,SCK默认为低电平。
在SCK由高电平切换为低电平时,主机和从机会移出数据,在由低电平切换为高电平时,主机和从机会移入数据,也就是读取数据,如下图所示。
4.完整时序
在从机的芯片手册中,一般会有相对应的指令集,其中的每个指令所对应的数据称为指令码。
SPI的通信时序一般为起始条件+发送指令码+需要进行交换的数据+终止条件。下文会通过读写W25Q64存储芯片来帮助读者进一步理解。
二、W25Q64
(1)简介
W25Q64是一款24位的非易失性存储器,也就是掉电不丢失,它有8M的存储空间。单片机可以外挂该芯片来拓展其存储空间。
(2)引脚功能图
(3)内部空间框图
如下图所示,整个芯片的存储空间为8M字节,从0x00 00 00 ~ 0x7F FF FF,将其分割成128块储存区域,每块区域为64Kb,再对块进行分割成若干扇区,扇区再分割为很多页。对W25Q64芯片的读写操作是基于页的。每页的大小为256字节。
8M >> 块 >> 扇区 >> 页
每页的存储空间为256字节,连续写入数据时,最多只能写入一页的数据,若超过256字节,超过的部分则会回到页首部分,对原数据进行覆盖。 写入操作前,必须先发送写使能指令 写入数据前必须先擦除,擦除后,所有数据位变为1。数据位只能从1变为0,而不能从0变为1。 写入操作结束后,芯片进入忙状态,不响应新的读写操作。 读取操作没有页数的限制,可以连续读取。
三、 软件模拟SPI
程序整体框架分为通信层、硬件驱动层、主函数
(1)通信层
1.通信引脚配置
SS片选信号线默认高电平, SCK时钟线默认低电平。
/* SS片选信号线 */
void MySPI_SS(uint8_t BitValue)
{
GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue);
}
/* SCK时钟线 */
void MySPI_SCK(uint8_t BitValue)
{
GPIO_WriteBit(GPIOA,GPIO_Pin_5,(BitAction)BitValue);
}
/* MOSI主机输出线 */
void MySPI_MOSI(uint8_t BitValue)
{
GPIO_WriteBit(GPIOA,GPIO_Pin_7,(BitAction)BitValue);
}
/* MISO主机输入线 */
uint8_t MySPI_MISO(void)
{
return GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6);
}
/* 通信引脚初始化 */
void MySPI_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
MySPI_SS(1);
MySPI_SCK(0);
}
2.SPI起始条件 和 终止条件
/* 通信开始信号 */
void MySPI_Start(void)
{
MySPI_SS(0);
}
/* 通信停止信号 */
void MySPI_Stop(void)
{
MySPI_SS(1);
}
3.SPI写入一个字节
/* 写入一个字节 */
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
uint8_t i, ByteReceive = 0x00;
for(i=0;i<8;i++)
{
MySPI_MOSI(ByteSend & 0x80 >> i);//从高到低依次移出数据的每一位
MySPI_SCK(1);//上升沿读取数据
if(MySPI_MISO() == 1)
{
ByteReceive |= (0x80 >> i);
}
MySPI_SCK(0);//下降沿移出数据
}
return ByteReceive;
}
(2)硬件驱动层
W25Q64.c
/* W25Q64初始化 */
void W25Q64_Init(void)
{
MySPI_Init();
}
/* W25Q64写使能函数 */
void W25Q64_WriteEnable(void)
{
MySPI_Start();
MySPI_SwapByte(W25Q64_WRITE_ENABLE);
MySPI_Stop();
}
/* W25Q64等待不忙函数 */
void W25Q64_WaitBusy(void)
{
uint32_t TimeOut = 100000;
MySPI_Start();
MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);
while((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01)
{
TimeOut--;
if(TimeOut == 0)
{
break;
}
}
MySPI_Stop();
}
/* W25Q64页编程函数 */
void W25Q64_PageProgram(uint32_t Address,uint8_t *DataArry,uint16_t Count)
{
uint16_t i;
W25Q64_WriteEnable();
MySPI_Start();
MySPI_SwapByte(W25Q64_PAGE_PROGRAM);
MySPI_SwapByte(Address >>= 16);
MySPI_SwapByte(Address >>= 8);
MySPI_SwapByte(Address);
for(i = 0; i < Count; i++)
{
MySPI_SwapByte(DataArry[i]);
}
MySPI_Stop();
W25Q64_WaitBusy();
}
/* W25Q64扇擦除函数 */
void W25Q64_Sector_erasure(uint32_t Address)
{
W25Q64_WriteEnable();
MySPI_Start();
MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);
MySPI_SwapByte(Address >>= 16);
MySPI_SwapByte(Address >>= 8);
MySPI_SwapByte(Address);
MySPI_Stop();
W25Q64_WaitBusy();
}
/* W25Q64读取数据函数 */
void W25Q64_ReadData(uint32_t Address,uint8_t *DataArry,uint32_t Count)
{
uint32_t i;
MySPI_Start();
MySPI_SwapByte(W25Q64_READ_DATA);
MySPI_SwapByte(Address >>= 16);
MySPI_SwapByte(Address >>= 8);
MySPI_SwapByte(Address);
for(i = 0; i < Count; i++)
{
DataArry[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);
}
MySPI_Stop();
}
写入字节函数的参数为芯片手册中的指令码 如下所示
#define W25Q64_WRITE_ENABLE 0x06
#define W25Q64_WRITE_DISABLE 0x04
#define W25Q64_READ_STATUS_REGISTER_1 0x05
#define W25Q64_READ_STATUS_REGISTER_2 0x35
#define W25Q64_WRITE_STATUS_REGISTER 0x01
#define W25Q64_PAGE_PROGRAM 0x02
#define W25Q64_QUAD_PAGE_PROGRAM 0x32
#define W25Q64_BLOCK_ERASE_64KB 0xD8
#define W25Q64_BLOCK_ERASE_32KB 0x52
#define W25Q64_SECTOR_ERASE_4KB 0x20
#define W25Q64_CHIP_ERASE 0xC7
#define W25Q64_ERASE_SUSPEND 0x75
#define W25Q64_ERASE_RESUME 0x7A
#define W25Q64_POWER_DOWN 0xB9
#define W25Q64_HIGH_PERFORMANCE_MODE 0xA3
#define W25Q64_CONTINUOUS_READ_MODE_RESET 0xFF
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID 0xAB
#define W25Q64_MANUFACTURER_DEVICE_ID 0x90
#define W25Q64_READ_UNIQUE_ID 0x4B
#define W25Q64_JEDEC_ID 0x9F
#define W25Q64_READ_DATA 0x03
#define W25Q64_FAST_READ 0x0B
#define W25Q64_FAST_READ_DUAL_OUTPUT 0x3B
#define W25Q64_FAST_READ_DUAL_IO 0xBB
#define W25Q64_FAST_READ_QUAD_OUTPUT 0x6B
#define W25Q64_FAST_READ_QUAD_IO 0xEB
#define W25Q64_OCTAL_WORD_READ_QUAD_IO 0xE3
#define W25Q64_DUMMY_BYTE 0xFF
芯片手册原图
(3)主函数
以下主函数为在0x000000内存处写入数据0x10、0x20、0x30
读写操作流程为:
- 初始化引脚状态
- 擦除对应扇区
- 在对应页写入数据
- 在对应页读取数据
uint8_t WriteArry[] = {0x10,0x20,0x30};//写入数组
uint8_t ReadArry[3];//读取数组
int main(void)
{
OLED_Init();
W25Q64_Init();
W25Q64_Sector_erasure(0x000000);
W25Q64_PageProgram(0x000000,WriteArry,3);
W25Q64_ReadData(0x000000,ReadArry,3);
OLED_ShowHexNum(0,0,ReadArry[0],2,OLED_8X16);
OLED_ShowHexNum(32,0,ReadArry[1],2,OLED_8X16);
OLED_ShowHexNum(64,0,ReadArry[2],2,OLED_8X16);
OLED_Update();
while (1)
{
}
}
实验现象:
总结
相比于硬件SPI,软件SPI的引脚会更加灵活,但响应速度会比硬件SPI慢。可根据实际应用需求,自由选择软件或硬件。
作者:专打逆风橘