当前位置: 首页 > news >正文

SeanLib系列函数库-W25QXX


查看其它库函数说明,请点击此处跳转到SeanLib主页

1. 本篇内容

本篇介绍一个器件库,针对W25Q系列的Flash存储芯片,本库将常用操作抽象出来,提供了读、写的基本操作,其中写操作会自动检测是否需要擦除,若需要擦除会选择最小的擦除单元。

2. 使用说明

本库包含W25QXX.h 和 W25QXX.lib 两个文件,同时依赖SeanLib国的Sean_Send、Sean_SendRecv、Sean_GetTick三个函数。
器件结构体如下:

typedefstruct{unsignedintTag;//提供一个变量,可存储一些额外的信息,如设备类型unsignedintCS_Pin;//指定该芯片使用的CS引脚的BITBAND地址unsignedintSize;//只读,Flash容量,单位为ByteunsignedcharManufacturer_ID;//只读,制造商ID,执行Get_ID后会被更新unsignedcharDevice_ID;//只读,器件ID,执行Get_ID后会被更新unsignedshortBackup;//未使用unsignedchar*Buffer;//为写操作开辟的内存,大小4K,当不处于写操作时,允许其它程序借用该内存void*ComDev;//提供一个万能指针,可存放与该器件通讯的SPI设备指针void(*Dispose)(void);//函数指针,释放设备占用的内存/******************************************************************************* * 功 能: 读制造商ID和器件ID,可用于检测芯片通讯是否正常 * 参 数: 无 * 返回值: 无 *******************************************************************************/void(*Get_ID)(void);/******************************************************************************* * 功 能: 全片擦除,耗时较长 * 参 数: 无 * 返回值: 无 *******************************************************************************/void(*Erase_Chip)(void);/******************************************************************************* * 功 能: 读数据函数 * 参 数: 数据指针、起始地址、读取长度 * 返回值: 成功返回1,失败返回0 *******************************************************************************/Sean_Result_t(*Read)(unsignedchar*,unsignedint,unsignedint);/******************************************************************************* * 功 能: 写数据函数,写函数会自动检测是否需要擦除,会选择最小的擦除方式 * 参 数: 数据指针、起始地址、写入长度 * 返回值: 成功返回1,失败返回0 *******************************************************************************/Sean_Result_t(*Write)(constunsignedchar*,unsignedint,unsignedint);}W25Qxx_t;

全片擦除的时间取决于容量,一般不需要执行全片擦除。
创建设备的函数声明如下:

/******************************************************************************* * 功 能: 创建ADS器件设备指针 * 参 数: * PinAddr_CS : 片选引脚,如 BITBAND_ADDR(GPIOD->ODR, 5) * MBits : Flash容量,单位为Mbit,如W25Q128为128MBits * 返回值:创建成功返回设备指针,否则返回空指针NULL *******************************************************************************/W25Qxx_t*NewW25Qxx(unsignedintPinAddr_CS,unsignedintMBits);

3. 示例代码

3.1 创建设备指针

voidW25QXX_Init(void){Flash=NewW25Qxx(BITBAND_ADDR(GPIOG->ODR,11),128);if(Flash==NULL){Error_Handle(1,"Creat flash IC failed!");}Flash->ComDev=&SPI_FLASH;}MSH_INIT_EXPORT(2,W25QXX_Init,”Creat Spi flash“);

3.2 读写操作

下面展示了在Flash芯片上挂载文件系统时,user_diskio.c中需要修改的代码,其实就是FATFS通过这几个函数来读写存储器:

/** * @brief Reads Sector(s) * @param pdrv: Physical drive number (0..) * @param *buff: Data buffer to store read data * @param sector: Sector address (LBA) * @param count: Number of sectors to read (1..128) * @retval DRESULT: Operation result */DRESULTUSER_read(BYTE pdrv,/* Physical drive nmuber to identify the drive */BYTE*buff,/* Data buffer to store read data */DWORD sector,/* Sector address in LBA */UINT count/* Number of sectors to read */){/* USER CODE BEGIN READ *///AddLog(LogType_Debug, "Read, Sector:%d, Count:%d", sector, count);sector*=512;//将扇区编号转换为存储器地址count*=512;//将扇区数量转换为数据数量if(Flash->Read(buff,sector,count)!=Sean_PASS){returnRES_ERROR;}returnRES_OK;/* USER CODE END READ */}/** * @brief Writes Sector(s) * @param pdrv: Physical drive number (0..) * @param *buff: Data to be written * @param sector: Sector address (LBA) * @param count: Number of sectors to write (1..128) * @retval DRESULT: Operation result */#if_USE_WRITE==1DRESULTUSER_write(BYTE pdrv,/* Physical drive nmuber to identify the drive */constBYTE*buff,/* Data to be written */DWORD sector,/* Sector address in LBA */UINT count/* Number of sectors to write */){/* USER CODE BEGIN WRITE *//* USER CODE HERE *///AddLog(LogType_Debug, "Write, Sector:%d, Count:%d", sector, count);sector*=512;//将扇区编号转换为存储器地址count*=512;//将扇区数量转换为数据数量if(Flash->Write(buff,sector,count)!=Sean_PASS){returnRES_ERROR;}returnRES_OK;/* USER CODE END WRITE */}#endif/* _USE_WRITE == 1 *//** * @brief I/O control operation * @param pdrv: Physical drive number (0..) * @param cmd: Control code * @param *buff: Buffer to send/receive control data * @retval DRESULT: Operation result */#if_USE_IOCTL==1DRESULTUSER_ioctl(BYTE pdrv,/* Physical drive nmuber (0..) */BYTE cmd,/* Control code */void*buff/* Buffer to send/receive control data */){/* USER CODE BEGIN IOCTL */DRESULT res=RES_OK;switch(cmd){caseCTRL_SYNC://AddLog(LogType_Debug, "SYNC");break;caseCTRL_TRIM:break;caseGET_BLOCK_SIZE://擦除块大小(扇区数)*(DWORD*)buff=8;//4096 / 512;break;caseGET_SECTOR_SIZE://扇区大小*(DWORD*)buff=512;break;caseGET_SECTOR_COUNT://总共的扇区数量*(DWORD*)buff=16384;//8 * 1024 * 1024 / 512;break;default:res=RES_PARERR;break;}returnres;/* USER CODE END IOCTL */}#endif/* _USE_IOCTL == 1 */

可以看到其中的读写数据调用的下面两个方法:

//从地址sector(非扇区地址,而是存储单元的地址)开始读取count个字节,写入到buff中Flash->Read(buff,sector,count);//从地址sector处开始写入buff中的数据,共写入count个字节Flash->Write(buff,sector,count)

不使用FATFS时,直接用上面这两个方法即可操作存储器。
GetID的方法:

// 读制造商ID和器件IDvoidFlash_GetID(char*Payload){Flash->Get_ID();Printf_To_PC("OK,%s,Manufacturer ID:%02X, Device ID:%02X\r\n@_@",__FUNCTION__,Flash->Manufacturer_ID,Flash->Device_ID);}MSH_CMD_EXPORT(Flash_GetID,"Get ID of W25Q128");

3.3 外部函数

打开 SeanLib.c 文件,找到 #ifdef USE_SendRecv,编写如下代码:

//当某些库需要使用外部数据发送和接收函数时,需要实现下面的函数#ifdefUSE_SendRecv//数据发送函数//参数:// Dev: 调用该函数的设备,函数中可根据该指针判断要将数据发送到哪个通讯端口// Data: 要发送的数据指针// Len: 要发送的数据长度//返回值:无voidSean_Send(void*Dev,unsignedchar*Data,unsignedshortLen){if(Dev==Flash){HAL_SPI_Transmit(((W25Qxx_t*)Dev)->ComDev,Data,Len,Len);}elseif(Dev==EtherNet){HAL_SPI_Transmit(((W5500_t*)Dev)->ComDev,Data,Len,Len);}}//数据接收和发送函数,适用于SPI通讯的设备//参数:// Dev: 调用该函数的设备,可根据该指针判断要将数据发送到哪个通讯端口// Send: 要发送的数据的存放指针// Recv: 接收到的数据的存放指针// Len: 传输的数据长度//返回值: 无voidSean_SendRecv(void*Dev,unsignedchar*Send,unsignedchar*Recv,unsignedshortLen){//示例:ADS1118芯片的SPI发送和接收函数// HAL_SPI_TransmitReceive(((ADS1118_t*)Dev)->ComDev, Send, Recv, Len, Len);//示例:ADS114芯片的SPI发送和接收函数// HAL_SPI_TransmitReceive(((ADS114_t*)Dev)->ComDev, Send, Recv, Len, Len);if(Dev==Flash){HAL_SPI_TransmitReceive(((W25Qxx_t*)Dev)->ComDev,Send,Recv,Len,Len);}elseif(Dev==EtherNet){HAL_SPI_TransmitReceive(((W5500_t*)Dev)->ComDev,Send,Recv,Len,Len);}}#endif

4. 其它说明

设备结构体中有一个指针:unsigned char * Buffer; 注释中说:为写操作开辟的内存,大小4K,当不处于写操作时,允许其它程序借用该内存
问:为什么需要这4K内存?
答:Flash的扇区大小为4K,要写入数据时,需要先将扇区中的数据全部读出来,再修改为新的数据,并将原扇区数据擦除,再写入新的数据,因此需要一个扇区的内存空间来保存原数据
问:我需要自己申请这块内存吗?
答:不需要,这块内存是在创建设备指针的时候自动分配的

http://www.jsqmd.com/news/710823/

相关文章:

  • 从LeetCode到真实项目:DAG(有向无环图)在任务调度和依赖管理中的实战避坑指南
  • 人工海马网络(AHN)架构解析与长序列处理优化
  • 写给Ivy(我自己你信吗:))啊······
  • Bibata Gruvbox Yellow光标主题:Linux桌面美化与视觉统一方案
  • 2026降AI率工具实测:AI占比90%也能稳降到个位数
  • 终极指南:用Ryujinx模拟器在电脑上免费畅玩Switch游戏的完整攻略
  • Java 基础(十一)反射
  • SILENTTRINITY:基于Python异步架构的现代C2渗透测试框架解析
  • Windows电脑终极指南:如何用APK安装器直接运行安卓应用
  • 【Python】错误和异常
  • 亲测5款论文降AI工具:AIGC疑似度从90%降到4%实用指南
  • LycheeMemory:高效处理长上下文任务的创新解决方案
  • 星穹铁道跃迁记录分析工具:5分钟掌握抽卡数据可视化
  • Git 命令大全测试
  • 后端全栈轻松写前端!用 Vue,自动生成可维护 React
  • 终极RPG Maker解密工具:如何快速提取游戏资源与项目文件
  • 别再只用filter: blur()了!聊聊backdrop-filter在Vue3音乐播放器项目中的实战应用
  • RAG 工程实践:分块策略、Rerank、混合检索,这些细节决定效果上限
  • 手机电池寿命翻倍秘诀:BatteryChargeLimit智能充电限制器
  • CQ 省集记录
  • MATLAB新手也能搞定:一步步教你用netCDF读取IPIX雷达海杂波数据(附完整代码)
  • 摩尔线程 x 中国移动|国产GPU率先支撑央企大模型,S5000完成九天35B大模型适配
  • 终极生态系统模拟器Ecosim:探索自然选择与进化的视觉盛宴
  • 大语言模型持续学习评估:OAKS框架解析与实践
  • 基于LoRA微调开源大模型,打造专业法律文本生成AI助手
  • 分组过滤:HAVING
  • [Openclaw] OpenClaw v2026.4.21 升级技术摘要
  • 如何提高网站收录?老手常用的自动推送接口配置
  • 下载 | Win10 2021官方精简版,预装应用极少!(4月更新、Win10 IoT LTSC 2021版、适合老电脑)
  • 黑马点评-短信登陆笔记