文章目录
- 前言
- 一、硬件电路
- 二、W25Q64初始化
- 三、W25Q64读数据
- 四、W25Q64写数据
前言
一、硬件电路
PA15为W25Q64片选线配置为GPIO输出模式
二、W25Q64初始化
通过SPI读取设备ID验证是否能正确读写
c
static int ReadID(void){
uint8_t cmd = 0x9F;
uint8_t data[3] = {0};
W25Q64CSLOW;
HAL_StatusTypeDef status = HAL_SPI_Transmit(&hspi1, &cmd, 1, 10);
if(status != HAL_OK){
LogDebug("ReadID HAL_SPI_Transmit ERROR\r\n");
W25Q64CSGIGH;
return -EIO;
}
status = HAL_SPI_Receive(&hspi1, data, 3, 10);
if(status != HAL_OK){
LogDebug("ReadID HAL_SPI_Receive ERROR\r\n");
W25Q64CSGIGH;
return -EIO;
}
W25Q64CSGIGH;
int id = (data[0]<<16) | (data[1]<<8) | (data[2]<<0);
return id;
}
static int W25Q64Init(void){
uint32_t id = ReadID();
if(id != 0xEF4017){
LogDebug("W25Q64Init ERROR\r\n");
return -EIO;
}
LogDebug("W25Q64Init id is %X\r\n",id);
}
三、W25Q64读数据
该指令 允许读出一个字节或一个以上的字节。先把 /CS引脚 拉低为低电平,然后把 03h 通过DI引脚 写入芯片,再送入 24位的地址,这些数据将在 CLK 的上升沿被芯片采集。芯片接收完 24位地址 之后,就会把相应地址的数据在 CLK引脚的下降沿从 DO引脚 发送出去,高位在前。当发送完这个地址的数据之后,地址将自动增加,然后通过 DO引脚把 下一个地址的数据发送出去,从而形成一个 数据流。也就是说,只要时钟在工作,通过 一条读指令,就可以把 整个芯片存储区的数据读出来。
c
static int W25Q64Read(uint32_t addr,uint8_t *pdata,uint32_t length){
uint8_t data[4] = {0};
data[0] = 0x03;
data[1] = (addr & 0xFF0000)>>16;
data[2] = (addr & 0x00FF00)>>8;
data[3] = (addr & 0x0000FF)>>0;
W25Q64CSLOW;
HAL_StatusTypeDef status = HAL_SPI_Transmit(&hspi1, data, 4, 10);
if(status != HAL_OK){
LogDebug("W25Q64Read HAL_SPI_Transmit ERROR\r\n");
W25Q64CSGIGH;
return -EIO;
}
status = HAL_SPI_Receive(&hspi1, pdata, length, 100);
if(status != HAL_OK){
LogDebug("W25Q64Read HAL_SPI_Receive ERROR\r\n");
W25Q64CSGIGH;
return -EIO;
}
W25Q64CSGIGH;
return ESUCCESS;
}
四、W25Q64写数据
- 写使能指令(06h)
该指令会使 状态寄存器WEL位置位。在执行每个页编程、扇区擦除、块擦除、芯片擦除和写状态寄存器等指令之前,都要先置位 WEL。/CS引脚 先拉低为低电平后,写使能指令代码 06h 从 DI引脚输入,在 CLK上升沿采集,然后将 /CS引脚 拉高为高电平。
c
//在页写和擦除等指令完成后 写使能位会被自动置0 因此每次要写数据之前必须开启写使能位
static int WriteEnable(void){
uint8_t cmd = 0x06;
W25Q64CSLOW;
HAL_StatusTypeDef status = HAL_SPI_Transmit(&hspi1, &cmd, 1, 10);
if(status != HAL_OK){
LogDebug("WriteEnable HAL_SPI_Transmit ERROR\r\n");
W25Q64CSGIGH;
return -EIO;
}
W25Q64CSGIGH;
return ESUCCESS;
}
//等待页写或者擦除完成
static int WaitWriteEnd(void){
uint8_t cmd = 0x05;
uint8_t registerStatus = 0;
W25Q64CSLOW;
HAL_StatusTypeDef status = HAL_SPI_Transmit(&hspi1, &cmd, 1, 10);
if(status != HAL_OK){
LogDebug("WriteEnable HAL_SPI_Transmit ERROR\r\n");
W25Q64CSGIGH;
return -EIO;
}
status = HAL_SPI_Receive(&hspi1, ®isterStatus, 1, 10);
if(status != HAL_OK){
LogDebug("WriteEnable HAL_SPI_Transmit ERROR\r\n");
W25Q64CSGIGH;
return -EIO;
}
while(registerStatus & 0x01){
status = HAL_SPI_Receive(&hspi1, ®isterStatus, 1, 10);
if(status != HAL_OK){
LogDebug("WriteEnable HAL_SPI_Transmit ERROR\r\n");
W25Q64CSGIGH;
return -EIO;
}
}
W25Q64CSGIGH;
return ESUCCESS;
}
- 写禁止指令(04h)
该指令将会使 WEL位 变为0。/CS引脚 拉低为低电平后,再把 04h 从 DI引脚 输入到芯片,将 /CS引脚 拉高为高电平后,就可完成这个指令。
在执行完 写状态寄存器、页编程、扇区擦除、块擦除、芯片擦除等指令之后,WEL位就会自动变为 0。
- 页写(0x02)
该指令会从指定地址的页开始写数据,当要写的数据超过当前页最大地址时,从该页的首地址覆盖写。
在页写之前要对该页进行擦除。因为W25Q64在存储数据时是将内存中的1置为0来实现的,所以在写数据时要先擦除写地址的数据。
c
static int PageWrite(uint32_t addr,uint8_t *pdata,uint32_t length){
if(length > 256)
length = 256;
uint8_t data[4] = {0};
data[0] = 0x02;
data[1] = (addr & 0xFF0000)>>16;
data[2] = (addr & 0x00FF00)>>8;
data[3] = (addr & 0x0000FF)>>0;
int ret = WriteEnable();
if(ret != ESUCCESS){
LogDebug("PageWrite WriteEnable ERROR\r\n");
return -EIO;
}
W25Q64CSLOW;
HAL_StatusTypeDef status = HAL_SPI_Transmit(&hspi1, data, 4, 10);
if(status != HAL_OK){
LogDebug("PageWrite HAL_SPI_Transmit ERROR\r\n");
W25Q64CSGIGH;
return -EIO;
}
status = HAL_SPI_Transmit(&hspi1, pdata, length, 100);
if(status != HAL_OK){
LogDebug("PageWrite HAL_SPI_Transmit ERROR\r\n");
W25Q64CSGIGH;
return -EIO;
}
W25Q64CSGIGH;
ret = WaitWriteEnd();
if(ret != ESUCCESS){
LogDebug("PageWrite WaitWriteEnd ERROR\r\n");
return -EIO;
}
return ESUCCESS;
}
- 扇区擦除
W25Q64擦除的最小单位为4K的扇区。
例如输入0-4095之间的地址,会将第一个扇区擦除。输入任意地址,会将该地址所处于的扇区擦除。
c
static int SectorErase(uint32_t addr){
uint8_t data[4] = {0};
data[0] = 0x20;
data[1] = (addr & 0xFF0000)>>16;
data[2] = (addr & 0x00FF00)>>8;
data[3] = (addr & 0x0000FF)>>0;
int ret = WriteEnable();
if(ret != ESUCCESS){
LogDebug("SectorErase WriteEnable ERROR\r\n");
return -EIO;
}
W25Q64CSLOW;
HAL_StatusTypeDef status = HAL_SPI_Transmit(&hspi1, data, 4, 10);
if(status != HAL_OK){
LogDebug("SectorErase HAL_SPI_Transmit ERROR\r\n");
W25Q64CSGIGH;
return -EIO;
}
W25Q64CSGIGH;
ret = WaitWriteEnd();
if(ret != ESUCCESS){
LogDebug("SectorErase WaitWriteEnd ERROR\r\n");
return -EIO;
}
return ESUCCESS;
}