W25Q64接线图

PS:因为我的W25Q64模块的引脚虚焊了,所以本节的代码,我使用的是STM32F407的开发板所实现的,在该开发板上面,使用的是W25Q128模块,和W25Q64大差不差。
SPI通信层
#include "stm32f4xx.h" // Device header
void MySPI_W_SS(uint8_t BitValue)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_14, (BitAction)(BitValue ? Bit_SET : Bit_RESET));
}
void MySPI_W_SCK(uint8_t BitValue)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_3, (BitAction)(BitValue ? Bit_SET : Bit_RESET));
}
void MySPI_W_MOSI(uint8_t BitValue)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_5, (BitAction)(BitValue ? Bit_SET : Bit_RESET));
}
uint8_t MySPI_R_MISO(void)
{
return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_4);
}
void MySPI_Init(void)
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
// 配置 SCK, MOSI, CS 引脚为输出
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_5 | GPIO_Pin_14; // SCK, MOSI, CS
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; // 输出模式
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; // 推挽输出
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; // 速度50MHz
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; // 无上下拉
GPIO_Init(GPIOB, &GPIO_InitStruct);
// 配置 MISO 引脚为输入
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4; // MISO
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; // 输入模式
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; // 上拉
GPIO_Init(GPIOB, &GPIO_InitStruct);
// 初始化 SPI 引脚状态
MySPI_W_SS(1); // SS 拉高,未选中
MySPI_W_SCK(0); // SCK 初始为低电平
}
void MySPI_Start(void)
{
MySPI_W_SS(0); // 将 CS 拉低,选中从设备
}
void MySPI_Stop(void)
{
MySPI_W_SS(1); // 将 CS 拉高,释放从设备
}
void Delay(void)
{
volatile uint32_t i;
for (i = 0; i < 50; i++); // 简单延时,具体值可根据需要调整
}
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
uint8_t i, ByteReceive = 0x00;
for (i = 0; i < 8; i++)
{
// 发送位(先发送高位)
MySPI_W_MOSI((ByteSend & 0x80) ? 1 : 0);
ByteSend <<= 1;
// 拉高时钟
MySPI_W_SCK(1);
Delay();
// 接收位
ByteReceive <<= 1;
if (MySPI_R_MISO())
{
ByteReceive |= 0x01;
}
// 拉低时钟
MySPI_W_SCK(0);
Delay();
}
return ByteReceive;
}
硬件驱动层
可以先做一个ReadID函数来读取芯片的ID,这样做是为了测试通信是否成功。
#include "stm32f4xx.h" // Device header
#include "MySPI.h"
void W25Q128_Init(void)
{
MySPI_Init();
}
//读取ID号时序:起始-随后连续交换3个字节-停止。
//第一个字节是厂商ID,后两个字节是设备ID,其中设备ID的高8位是存储器类型、低8位表示容量。
void W25Q128_ReadID(uint8_t *MID,uint16_t *DID)
{
MySPI_Start();
MySPI_SwapByte(0x9F);
*MID = MySPI_SwapByte(0xFF);
*DID = MySPI_SwapByte(0XFF);
*DID <<= 8;
*DID |= MySPI_SwapByte(0xFF);
MySPI_Stop();
}
//获取Busy位,查询芯片是否繁忙。
void W25Q128_WaitBusy(void)
{
uint32_t Timeout;
MySPI_Start();
MySPI_SwapByte(W25Q128_READ_STATUS_REGISTER_1);
Timeout = 100000;
//如果Busy位为1,就会进入循环,再次读出一次状态寄存器
while((MySPI_SwapByte(W25Q128_DUMMY_BYTE) & 0x01) == 0x01)
{
Timeout--;
if(Timeout == 0)
{
break;
}
}
MySPI_Stop();
}
//页编程
void W25Q128_Pageprogram(uint32_t Address,uint8_t *DataArray,uint16_t Count)
{
//写使能
W25Q128_WriteEnable();
uint16_t i;
MySPI_Start();
//发送地址
MySPI_SwapByte(W25Q128_PAGE_PROGRAM);
MySPI_SwapByte(Address >> 16);
MySPI_SwapByte(Address >> 8);
MySPI_SwapByte(Address);
//发送数据到芯片中
for(i=0 ; i<Count ;i++)
{
MySPI_SwapByte(DataArray[i]);
}
MySPI_Stop();
//等待写入,写入成功后,Busy位置0
W25Q128_WaitBusy();
}
//擦除所在地址扇区(4K)
void W25Q128_SectorErase(uint32_t Address)
{
//写使能
W25Q128_WriteEnable();
MySPI_Start();
//发送地址
MySPI_SwapByte(W25Q128_SECTOR_ERASE_4KB);
MySPI_SwapByte(Address >> 16);
MySPI_SwapByte(Address >> 8);
MySPI_SwapByte(Address);
MySPI_Stop();
//等待写入,写入成功后,Busy位置0
W25Q128_WaitBusy();
}
//读取数据
void W25Q128_ReadData(uint32_t Address,uint8_t *DataArray,uint32_t Count)
{
uint32_t i;
MySPI_Start();
//发送地址
MySPI_SwapByte(W25Q128_READ_DATA);
MySPI_SwapByte(Address >> 16);
MySPI_SwapByte(Address >> 8);
MySPI_SwapByte(Address);
//接收数据到数组中
for(i=0;i<Count;i++)
{
DataArray[i] = MySPI_SwapByte(W25Q128_DUMMY_BYTE);
}
MySPI_Stop();
}
W25Q128指令
W25Q128_WRITE_ENABLE 0x06
W25Q128_WRITE_DISABLE 0x04
W25Q128_READ_STATUS_REGISTER_1 0x05
W25Q128_READ_STATUS_REGISTER_2 0x35
W25Q128_WRITE_STATUS_REGISTER 0x01
W25Q128_PAGE_PROGRAM 0x02
W25Q128_QUAD_PAGE_PROGRAM 0x32
W25Q128_BLOCK_ERASE_64KB 0xD8
W25Q128_BLOCK_ERASE_32KB 0x52
W25Q128_SECTOR_ERASE_4KB 0x20
W25Q128_CHIP_ERASE 0xC7
W25Q128_ERASE_SUSPEND 0x75
W25Q128_ERASE_RESUME 0x7A
W25Q128_POWER_DOWN 0xB9
W25Q128_HIGH_PERFORMANCE_MODE 0xA3
W25Q128_CONTINUOUS_READ_MODE_RESET 0xFF
W25Q128_RELEASE_POWER_DOWN_HPM_DEVICE_ID 0xAB
W25Q128_MANUFACTURER_DEVICE_ID 0x90
W25Q128_READ_UNIQUE_ID 0x4B
W25Q128_JEDEC_ID 0x9F
W25Q128_READ_DATA 0x03
W25Q128_FAST_READ 0x0B
W25Q128_FAST_READ_DUAL_OUTPUT 0x3B
W25Q128_FAST_READ_DUAL_IO 0xBB
W25Q128_FAST_READ_QUAD_OUTPUT 0x6B
W25Q128_FAST_READ_QUAD_IO 0xEB
W25Q128_OCTAL_WORD_READ_QUAD_IO 0xE3
W25Q128_DUMMY_BYTE 0xFF
写代码时的注意事项:
一定要记住,写入数据(擦除实际上也是写入)的时候,要记得等待写入完成Busy位,要加一个等待Busy。