w25q64.h
#ifndef__W25Q64_H#define__W25Q64_H#include"main.h"#include"spi.h"// 引脚定义#defineW25Q64_CS_PINGPIO_PIN_15#defineW25Q64_CS_PORTGPIOA// W25Q64指令集#defineW25Q64_WRITE_ENABLE0x06#defineW25Q64_WRITE_DISABLE0x04#defineW25Q64_READ_STATUS10x05#defineW25Q64_WRITE_STATUS10x01#defineW25Q64_READ_DATA0x03#defineW25Q64_PAGE_PROGRAM0x02#defineW25Q64_SECTOR_ERASE0x20#defineW25Q64_CHIP_ERASE0xC7#defineW25Q64_POWER_DOWN0xB9#defineW25Q64_RELEASE_PD0xAB#defineW25Q64_MANUF_DEVICE_ID0x90#defineW25Q64_JEDEC_ID0x9F// Flash容量参数#defineW25Q64_PAGE_SIZE256#defineW25Q64_SECTOR_SIZE4096#defineW25Q64_TOTAL_SIZE(8*1024*1024)// 8MB// 函数声明voidW25Q64_Init(void);uint8_tW25Q64_ReadByte(uint32_taddr);voidW25Q64_ReadBytes(uint32_taddr,uint8_t*buf,uint32_tlen);voidW25Q64_WriteByte(uint32_taddr,uint8_tdata);voidW25Q64_WriteBytes(uint32_taddr,uint8_t*data,uint32_tlen);voidW25Q64_ModifyByte(uint32_taddr,uint8_tdata);voidW25Q64_EraseSector(uint32_tsector_addr);voidW25Q64_WaitBusy(void);uint32_tW25Q64_ReadID(void);#endif/* __W25Q64_H */w25q64.c
#include"w25q64.h"// 私有函数声明staticvoidW25Q64_CS_Low(void);staticvoidW25Q64_CS_High(void);staticvoidW25Q64_WriteEnable(void);staticuint8_tW25Q64_ReadStatus(void);// 初始化函数voidW25Q64_Init(void){W25Q64_CS_High();HAL_Delay(10);// 读取ID验证连接uint32_tid=W25Q64_ReadID();if(id==0xEF4017)// W25Q64JV的JEDEC ID{// 初始化成功}}// 读取单个字节uint8_tW25Q64_ReadByte(uint32_taddr){uint8_tcmd[4]={W25Q64_READ_DATA,(addr>>16)&0xFF,(addr>>8)&0xFF,addr&0xFF};uint8_tdata=0;W25Q64_CS_Low();HAL_SPI_Transmit(&hspi6,cmd,4,100);HAL_SPI_Receive(&hspi6,&data,1,100);W25Q64_CS_High();returndata;}// 读取多个字节voidW25Q64_ReadBytes(uint32_taddr,uint8_t*buf,uint32_tlen){uint8_tcmd[4]={W25Q64_READ_DATA,(addr>>16)&0xFF,(addr>>8)&0xFF,addr&0xFF};W25Q64_CS_Low();HAL_SPI_Transmit(&hspi6,cmd,4,100);HAL_SPI_Receive(&hspi6,buf,len,1000);W25Q64_CS_High();}// 写入单个字节voidW25Q64_WriteByte(uint32_taddr,uint8_tdata){uint8_tcmd[5]={W25Q64_PAGE_PROGRAM,(addr>>16)&0xFF,(addr>>8)&0xFF,addr&0xFF,data};W25Q64_WriteEnable();W25Q64_CS_Low();HAL_SPI_Transmit(&hspi6,cmd,5,100);W25Q64_CS_High();W25Q64_WaitBusy();}// 写入多个字节(注意:不能跨页写入)voidW25Q64_WriteBytes(uint32_taddr,uint8_t*data,uint32_tlen){// 确保不跨页写入uint32_tpage_remain=W25Q64_PAGE_SIZE-(addr%W25Q64_PAGE_SIZE);uint32_twrite_len=(len>page_remain)?page_remain:len;uint8_t*cmd=(uint8_t*)malloc(write_len+4);if(cmd==NULL)return;cmd[0]=W25Q64_PAGE_PROGRAM;cmd[1]=(addr>>16)&0xFF;cmd[2]=(addr>>8)&0xFF;cmd[3]=addr&0xFF;memcpy(&cmd[4],data,write_len);W25Q64_WriteEnable();W25Q64_CS_Low();HAL_SPI_Transmit(&hspi6,cmd,write_len+4,1000);W25Q64_CS_High();W25Q64_WaitBusy();free(cmd);// 如果还有数据需要写入,递归调用if(len>write_len){W25Q64_WriteBytes(addr+write_len,data+write_len,len-write_len);}}// 修改指定地址的字节(先擦除后写入)voidW25Q64_ModifyByte(uint32_taddr,uint8_tdata){// 计算所在扇区uint32_tsector_addr=addr-(addr%W25Q64_SECTOR_SIZE);uint8_tsector_buffer[W25Q64_SECTOR_SIZE];// 1. 读取整个扇区W25Q64_ReadBytes(sector_addr,sector_buffer,W25Q64_SECTOR_SIZE);// 2. 修改指定字节sector_buffer[addr-sector_addr]=data;// 3. 擦除扇区W25Q64_EraseSector(sector_addr);// 4. 重新写入整个扇区// 分页写入,每次写入一页(256字节)for(uint32_ti=0;i<W25Q64_SECTOR_SIZE;i+=W25Q64_PAGE_SIZE){W25Q64_WriteBytes(sector_addr+i,§or_buffer[i],W25Q64_PAGE_SIZE);}}// 擦除扇区(4KB)voidW25Q64_EraseSector(uint32_tsector_addr){uint8_tcmd[4]={W25Q64_SECTOR_ERASE,(sector_addr>>16)&0xFF,(sector_addr>>8)&0xFF,sector_addr&0xFF};W25Q64_WriteEnable();W25Q64_CS_Low();HAL_SPI_Transmit(&hspi6,cmd,4,100);W25Q64_CS_High();W25Q64_WaitBusy();}// 等待Flash操作完成voidW25Q64_WaitBusy(void){while(W25Q64_ReadStatus()&0x01){HAL_Delay(1);}}// 读取JEDEC IDuint32_tW25Q64_ReadID(void){uint8_tcmd=W25Q64_JEDEC_ID;uint8_tid_data[3];W25Q64_CS_Low();HAL_SPI_Transmit(&hspi6,&cmd,1,100);HAL_SPI_Receive(&hspi6,id_data,3,100);W25Q64_CS_High();return(id_data[0]<<16)|(id_data[1]<<8)|id_data[2];}// 私有函数实现staticvoidW25Q64_CS_Low(void){HAL_GPIO_WritePin(W25Q64_CS_PORT,W25Q64_CS_PIN,GPIO_PIN_RESET);}staticvoidW25Q64_CS_High(void){HAL_GPIO_WritePin(W25Q64_CS_PORT,W25Q64_CS_PIN,GPIO_PIN_SET);}staticvoidW25Q64_WriteEnable(void){uint8_tcmd=W25Q64_WRITE_ENABLE;W25Q64_CS_Low();HAL_SPI_Transmit(&hspi6,&cmd,1,100);W25Q64_CS_High();}staticuint8_tW25Q64_ReadStatus(void){uint8_tcmd=W25Q64_READ_STATUS1;uint8_tstatus;W25Q64_CS_Low();HAL_SPI_Transmit(&hspi6,&cmd,1,100);HAL_SPI_Receive(&hspi6,&status,1,100);W25Q64_CS_High();returnstatus;}使用例程
#include"w25q64.h"// 测试函数voidmain(void){uint8_tread_data;uint8_twrite_data=0xAA;uint32_ttest_addr=0x000000;// 测试地址// 1. 初始化W25Q64_Init();// 2. 写入单个字节W25Q64_WriteByte(test_addr,write_data);HAL_Delay(10);// 3. 读取单个字节read_data=W25Q64_ReadByte(test_addr);if(read_data==write_data){// 写入成功}// 4. 批量写入测试uint8_twrite_buffer[256];uint8_tread_buffer[256];for(inti=0;i<256;i++){write_buffer[i]=i;}W25Q64_WriteBytes(0x1000,write_buffer,256);HAL_Delay(10);// 5. 批量读取测试W25Q64_ReadBytes(0x1000,read_buffer,256);// 6. 修改指定地址的数据W25Q64_ModifyByte(0x2000,0x55);// 7. 擦除扇区测试W25Q64_EraseSector(0x3000);}CubeMX配置要点
- SPI6配置:
· Mode: Full-Duplex Master
· Hardware NSS Signal: Disable
· Prescaler: 根据HCLK频率调整(建议≤50MHz) - GPIO配置:
· PA15: GPIO_Output(CS引脚)
· PB3: SPI6_SCK
· PB4: SPI6_MISO
· PB5: SPI6_MOSI - 时钟配置:
· 确保SPI时钟不超过W25Q64的最大频率(104MHz)
注意事项
- 写操作前必须擦除:Flash只能从1变为0,所以写前需要擦除(变为0xFF)
- 擦除最小单位:4KB扇区
- 写操作跨页处理:页大小为256字节,跨页时需要分页写入
- 修改函数效率:W25Q64_ModifyByte会擦除整个扇区,频繁修改时应考虑缓存机制
这个驱动提供了基本的读写和修改功能,可以根据需要扩展更多功能(如整片擦除、快速读取等)。