立创梁山派--移植开源的SFUD万能的串行 Flash 通用驱动库

SFUD是什么

关于SFUD库的介绍,其开源链接(gitee,github)已经详细的阐述了.
这里是截取自它的一部分介绍:
SFUD 是一款开源的串行 SPI Flash 通用驱动库。由于现有市面的串行 Flash 种类居多,各个 Flash 的规格及命令存在差异, SFUD 就是为了解决这些 Flash 的差异现状而设计,让我们的产品能够支持不同品牌及规格的 Flash,提高了涉及到 Flash 功能的软件的可重用性及可扩展性,同时也可以规避 Flash 缺货或停产给产品所带来的风险。

主要特点:支持 SPI/QSPI 接口、面向对象(同时支持多个 Flash 对象)、可灵活裁剪、扩展性强、支持 4 字节地址
资源占用
标准占用:RAM:0.2KB ROM:5.5KB
最小占用:RAM:0.1KB ROM:3.6KB
设计思路:
什么是 SFDP :它是 JEDEC (固态技术协会)制定的串行 Flash 功能的参数表标准,最新版 V1.6B (点击这里查看)。该标准规定了,每个 Flash 中会存在一个参数表,该表中会存放 Flash 容量、写粒度、擦除命令、地址模式等 Flash 规格参数。目前,除了部分厂家旧款 Flash 型号会不支持该标准,其他绝大多数新出厂的 Flash 均已支持 SFDP 标准。所以该库在初始化时会优先读取 SFDP 表参数。
不支持 SFDP 怎么办 :如果该 Flash 不支持 SFDP 标准,SFUD 会查询配置文件 ( /sfud/inc/sfud_flash_def.h ) 中提供的 Flash 参数信息表 中是否支持该款 Flash。如果不支持,则可以在配置文件中添加该款 Flash 的参数信息(添加方法详细见 2.5 添加库目前不支持的 Flash)。获取到了 Flash 的规格参数后,就可以实现对 Flash 的全部操作。
详细的可以查看开源链接(gitee,github);

为什么选择 SFUD

避免项目因 Flash 缺货、Flash 停产或产品扩容而带来的风险;
越来越多的项目将固件存储到串行 Flash 中,例如:ESP8266 的固件、主板中的 BIOS 及其他常见电子产品中的固件等等,但是各种 Flash 规格及命令不统一。使用 SFUD 即可避免,在相同功能的软件平台基础下,无法适配不同 Flash 种类的硬件平台的问题,提高软件的可重用性;
简化软件流程,降低开发难度。现在只需要配置好 SPI 通信,即可畅快的开始玩串行 Flash 了;
可以用来制作 Flash 编程器/烧写器

开始copy代码

这篇文章的重点就是来移植这个库,所以我就不多介绍了,直接开搞。

  • 首先先下载好立创梁山派附带的资料,等下要用到里面的提供的demo来作为我们的工程模板。
  • 我们使用 005-串口打印信息 这个作为我们的工程模板,然后在从015_spi中复制一份spi的代码,作为我们的flash驱动代码,还要下载一份sfud的源代码。
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
总共就是以上三个东西就ok啦。

  • 接下啦,按照下图,将文件添加到你的工程中进来,如果这一部分操作不会的话,可以学一下立创推出的教程,我这里就不再赘述啦。
    在这里插入图片描述
  • 接下来就是对代码进行小小的修改
    对bsp_spi.c文件按照我自己的代码风格进行了小小的修改和裁剪,因为这个SFUD十分完善,只需要我们提供这个spi的读取接口函数就ok了。
#include "bsp_spi.h"void bsp_spi4_init(void)
{//SPI参数定义结构体spi_parameter_struct spi_init_struct;rcu_periph_clock_enable(RCU_GPIOF);  // 使用F端口rcu_periph_clock_enable(RCU_SPI4);     // 使能SPI4//引脚复用gpio_af_set(GPIOF, GPIO_AF_5, GPIO_PIN_7);gpio_af_set(GPIOF, GPIO_AF_5, GPIO_PIN_8);gpio_af_set(GPIOF, GPIO_AF_5, GPIO_PIN_9);//引脚模式gpio_mode_set(GPIOF, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);gpio_mode_set(GPIOF, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8);gpio_mode_set(GPIOF, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9);//输出模式gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_7);gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8);gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);//开启CS引脚时钟rcu_periph_clock_enable(RCU_GPIOF);//配置CS引脚模式gpio_mode_set(GPIOF, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_PIN_6);//配置CS输出模式gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6);//W25Q64不选中gpio_bit_write(GPIOF, GPIO_PIN_6, SET);spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX;  	// 传输模式全双工spi_init_struct.device_mode          = SPI_MASTER;                	// 配置为主机spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;        	// 8位数据spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE;		//极性相位  spi_init_struct.nss                  = SPI_NSS_SOFT;              	//软件csspi_init_struct.prescale             = SPI_PSC_2;                 	//SPI时钟预分频为2spi_init_struct.endian               = SPI_ENDIAN_MSB;            	//高位在前//将参数填入SPI4spi_init(SPI4, &spi_init_struct);//使能SPIspi_enable(SPI4);
}/******************************************************************* 函 数 名 称:spi_read_write_byte* 函 数 说 明:硬件SPI的读写* 函 数 形 参:dat=发送的数据* 函 数 返 回:读取到的数据* 作       者:LCKFB* 备       注:无
******************************************************************/
uint8_t spi4_read_write_byte(uint8_t dat)
{//等待发送缓冲区为空while(RESET == spi_i2s_flag_get(SPI4,  SPI_FLAG_TBE) );spi_i2s_data_transmit(SPI4, dat);//等待接收缓冲区为空while(RESET == spi_i2s_flag_get(SPI4,  SPI_FLAG_RBNE) );return spi_i2s_data_receive(SPI4);
}
uint16_t spi4_flash_readID(void)
{uint16_t  temp = 0;	  	gpio_bit_write(GPIOF, GPIO_PIN_6, RESET);spi4_read_write_byte(0x90);//发送读取ID命令	    spi4_read_write_byte(0x00); 	    spi4_read_write_byte(0x00); 	    spi4_read_write_byte(0x00); 		//接收数据temp |= spi4_read_write_byte(0xFF)<<8;  temp |= spi4_read_write_byte(0xFF);	gpio_bit_write(GPIOF, GPIO_PIN_6, SET);	return temp;
}void spi4_w25qxx_cs(uint8_t enable)
{if(enable)gpio_bit_write(GPIOF, GPIO_PIN_6, SET);	elsegpio_bit_write(GPIOF, GPIO_PIN_6, RESET);	
}

对应的bsp_spi.h文件如下:

#ifndef _BSP_SPI_H
#define _BSP_SPI_H#include "gd32f4xx.h"
#include "systick.h"void bsp_spi4_init(void);uint16_t spi4_flash_readID(void);
uint8_t spi4_read_write_byte(uint8_t dat);
void spi4_w25qxx_cs(uint8_t enable);
#endif
  • 接下来是对这个sfud的移植
    这个是对于sfud_cfg.h的修改
    在这里插入图片描述

  • 剩下对一个sfud_port.c文件进行修改


#include <sfud.h>
#include <stdarg.h>#include "bsp_spi.h"
#include <string.h>#define OS 0 // 0:不使用OS,1:使用OS//这里使用的是freertos,你也可以换成你熟悉的rtos
#if OS  
#include "FreeRTOS.h"
#include "semphr.h"
#include "task.h"
#endifstatic char log_buf[256];void sfud_log_debug(const char *file, const long line, const char *format, ...);//这里是自添加的函数,实行对应接口的写buf函数
static void spi_flash_buf_write(const uint8_t *buf, size_t len)
{while (len--){spi4_read_write_byte(*buf);buf++;}
}//这里是自添加的函数,实行对应接口的读buf函数
static void spi_flash_buf_read(uint8_t *buf, size_t len)
{while (len--){*buf = spi4_read_write_byte(0xff);buf++;}
}/*** SPI write data then read data*/
static sfud_err spi_write_read(const sfud_spi *spi, const uint8_t *write_buf, size_t write_size, uint8_t *read_buf,size_t read_size)
{sfud_err result = SFUD_SUCCESS;//    uint8_t send_data, read_data;/*** add your spi write and read code*///这里就是我们要自行添加的代码if (write_size)SFUD_ASSERT(write_buf);if (read_size)SFUD_ASSERT(read_buf);spi4_w25qxx_cs(0);if (write_size){spi_flash_buf_write(write_buf, write_size);}if (read_size){spi_flash_buf_read(read_buf, read_size);}spi4_w25qxx_cs(1);return result;
}#ifdef SFUD_USING_QSPI
/*** read flash data by QSPI*/
static sfud_err qspi_read(const struct __sfud_spi *spi, uint32_t addr, sfud_qspi_read_cmd_format *qspi_read_cmd_format,uint8_t *read_buf, size_t read_size)
{sfud_err result = SFUD_SUCCESS;/*** add your qspi read flash data code*/return result;
}
#endif /* SFUD_USING_QSPI */#if OSstatic SemaphoreHandle_t sfudMutexSemaphor;
static void spi_lock_init(void)
{sfudMutexSemaphor = xSemaphoreCreateMutex();if (sfudMutexSemaphor == NULL){SFUD_DEBUG("sfud semaphor create failed");}
}
#endifstatic void spi_lock(const sfud_spi *spi)
{
#if OSif (sfudMutexSemaphor != NULL){xSemaphoreTake(sfudMutexSemaphor, portMAX_DELAY);}
#else__disable_irq(); //? ?  ж 
#endif
}
static void spi_unlock(const sfud_spi *spi)
{
#if OSif (sfudMutexSemaphor != NULL){xSemaphoreGive(sfudMutexSemaphor);}
#else__enable_irq();  //? ?  ж 
#endif
}
static void spi_delay(void)
{
#if OSvTaskDelay(1);
#elseuint32_t delay = 120;while(delay--);
#endif
}
sfud_err sfud_spi_port_init(sfud_flash *flash)
{sfud_err result = SFUD_SUCCESS;/*** add your port spi bus and device object initialize code like this:* 1. rcc initialize* 2. gpio initialize* 3. spi device initialize* 4. flash->spi and flash->retry item initialize*    flash->spi.wr = spi_write_read; //Required*    flash->spi.qspi_read = qspi_read; //Required when QSPI mode enable*    flash->spi.lock = spi_lock;*    flash->spi.unlock = spi_unlock;*    flash->spi.user_data = &spix;*    flash->retry.delay = null;*    flash->retry.times = 10000; //Required*///这里也是我们要添加的函数,这里要重点注意这个SPI4,要和sfud_cfg.h中保持一致if (!strcmp(flash->spi.name, "SPI4")){bsp_spi4_init();
#if OSspi_lock_init();
#endifflash->spi.wr = spi_write_read;flash->spi.lock = spi_lock;flash->spi.unlock = spi_unlock;flash->retry.delay = spi_delay;flash->retry.times = 60*10000;}return result;
}/*** This function is print debug info.** @param file the file which has call this function* @param line the line number which has call this function* @param format output format* @param ... args*/
void sfud_log_debug(const char *file, const long line, const char *format, ...)
{va_list args;/* args point to the first variable parameter */va_start(args, format);printf("[SFUD](%s:%ld) ", file, line);/* must use vprintf to print */vsnprintf(log_buf, sizeof(log_buf), format, args);printf("%s\n", log_buf);va_end(args);
}/*** This function is print routine info.** @param format output format* @param ... args*/
void sfud_log_info(const char *format, ...)
{va_list args;/* args point to the first variable parameter */va_start(args, format);printf("[SFUD]");/* must use vprintf to print */vsnprintf(log_buf, sizeof(log_buf), format, args);printf("%s\n", log_buf);va_end(args);
}
  • 上面的代码所示,其实也只需要修改 spi_write_readsfud_spi_port_init这两个函数,就移植完成啦。

接下来我们对移植完成的sfud库进行擦除,读,写功能的测试。

对应的main函数代码。


#include "main.h"
#include "bsp_led.h"
#include "bsp_usart.h"
#include "gd32f4xx.h"
#include "sys.h"
#include "systick.h"
#include <stdio.h>#include "bsp_spi.h"
#include <sfud.h>#define SFUD_DEMO_TEST_BUFFER_SIZE 1024static void sfud_demo(uint32_t addr, size_t size, uint8_t *data);static uint8_t sfud_demo_test_buf[SFUD_DEMO_TEST_BUFFER_SIZE];/*!\brief    main function\param[in]  none\param[out] none\retval     none
*/
int main(void)
{systick_config();led_gpio_config(); // led初始化usart_gpio_config(115200U);/* SFUD initialize */if (sfud_init() == SFUD_SUCCESS){sfud_demo(0, sizeof(sfud_demo_test_buf), sfud_demo_test_buf);}while (1){}
}
/*** SFUD demo for the first flash device test.** @param addr flash start address* @param size test flash size* @param size test flash data buffer*/
static void sfud_demo(uint32_t addr, size_t size, uint8_t *data) {sfud_err result = SFUD_SUCCESS;const sfud_flash *flash = sfud_get_device_table() + 0;size_t i;/* prepare write data */for (i = 0; i < size; i++) {data[i] = i;}/* erase test */result = sfud_erase(flash, addr, size);if (result == SFUD_SUCCESS) {printf("Erase the %s flash data finish. Start from 0x%08X, size is %ld.\r\n", flash->name, addr,size);} else {printf("Erase the %s flash data failed.\r\n", flash->name);return;}/* write test */result = sfud_write(flash, addr, size, data);if (result == SFUD_SUCCESS) {printf("Write the %s flash data finish. Start from 0x%08X, size is %ld.\r\n", flash->name, addr,size);} else {printf("Write the %s flash data failed.\r\n", flash->name);return;}/* read test */result = sfud_read(flash, addr, size, data);if (result == SFUD_SUCCESS) {printf("Read the %s flash data success. Start from 0x%08X, size is %ld. The data is:\r\n", flash->name, addr,size);printf("Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\r\n");for (i = 0; i < size; i++) {if (i % 16 == 0) {printf("[%08X] ", addr + i);}printf("%02X ", data[i]);if (((i + 1) % 16 == 0) || i == size - 1) {printf("\r\n");}}printf("\r\n");} else {printf("Read the %s flash data failed.\r\n", flash->name);}/* data check */for (i = 0; i < size; i++) {if (data[i] != i % 256) {printf("Read and check write data has an error. Write the %s flash data failed.\r\n", flash->name);break;}}if (i == size) {printf("The %s flash test is success.\r\n", flash->name);}
}

效果如下

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
擦除读写1k字节数据正常,到这里就移植完成啦。
至于如何使用sfud库,可以参考这个测试demo的用法,记得要向flash写入数据,要先擦除哦!

相关的代码链接:https://gitee.com/qingmang-pavilion/open_code/tree/master/lsp_sfud,如果对你有所帮助的话,请给我点个star吧,嘿嘿;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/47975.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一次搞定!中级软件设计师备考通关秘籍

大家好&#xff0c;我是小欧&#xff01; 今天我们来聊聊软考这个话题。要是你准备参加计算机技术与软件专业技术资格&#xff08;软考&#xff09;&#xff0c;那么这篇文章就是为你量身定做的。话不多说&#xff0c;咱们直接进入正题。 什么是软考&#xff1f; 软考&#xf…

请你谈谈:spring bean的生命周期 - 阶段4:检查Aware相关接口

在Spring框架中&#xff0c;Aware 接口系列提供了一种机制&#xff0c;允许bean在初始化过程中感知到容器中的特定对象&#xff0c;如应用上下文&#xff08;ApplicationContext&#xff09;、Bean工厂&#xff08;BeanFactory&#xff09;等。如果你有一个用户自定义的对象&am…

OpenSNN今日快讯:新型 54 轴仿人机器人、中国知网 CNKI 宣布上线新版首页、手搓复现GPT-2最初完整版本

一、科技视频资讯 人工智能】手搓复现GPT-2最初完整版本 | Andrej Karpathy | 8张H100训练24小时 | 成本仅672美元 | llm.c | C/CUDA | AI成本会下降么&#xff08;2024.7.17&#xff09; 简介&#xff1a;OpenAI的创始成员、前研究科学家安德烈卡帕西Andrej Karpathy最近又开始…

基于词级ngram的词袋模型对twitter数据进行情感分析

按照阿光的项目做出了学习笔记&#xff0c;pytorch深度学习实战项目100例 基于词级ngram的词袋模型对twitter数据进行情感分析 什么是 N 符&#xff1f; N 格是指给定文本或语音样本中 n 个项目的连续序列。这些项目可以是音素、音节、字母、单词或碱基对&#xff0c;具体取…

php 存储复杂的json格式查询(如:经纬度)

在开发中&#xff0c;有时我们可能存了一些复杂json格式不知道怎么查。我这里提供给大家参考下&#xff1a; 一、先上表数据格式&#xff08;location字段的possiton经纬度以逗号分开的&#xff09; {"title":"澳海文澜府","position":"11…

redis高可用之主从复制、哨兵以及Cluster集群

目录 一、Redis主从复制 1&#xff09;主从复制的作用 2&#xff09;主从复制流程 3&#xff09;搭建Redis主从复制 1、部署redis服务器 2、修改Redis配置文件&#xff08;所有节点操作&#xff09; 3、验证主从复制结果 二、哨兵模式 1&#xff09;哨兵的作用 2&…

ubuntu23安装tensorRT步骤记录

服务器信息&#xff1a; 1. ssh 连接信息&#xff1a;127.0.0.1 zhangsan2. 操作系统&#xff1a;Ubuntu 23.103. 显卡信息&#xff1a;NVIDIA Corporation GA102 [GeForce RTX 3090]4. cpu 架构&#xff1a;x86_64&#xff0c;13th Gen Intel(R) Core(TM) i9-13900( 使…

2401. 最长优雅子数组

Powered by:NEFU AB-IN Link 文章目录 2401. 最长优雅子数组题意思路代码 2401. 最长优雅子数组 题意 给你一个由 正 整数组成的数组 nums 。 如果 nums 的子数组中位于 不同 位置的每对元素按位 与&#xff08;AND&#xff09;运算的结果等于 0 &#xff0c;则称该子数组为…

微信小程序关于助力微短剧行业高质量发展的公告

微短剧行业已进入高质量发展的新阶段&#xff0c;在国家广播电视总局和广东省广播电视局的指导下&#xff0c;微信小程序平台始终坚持合规先行&#xff0c;主动建设行业管理规范&#xff0c;发布了《微短剧行业管理规范》&#xff0c;全面加强对于微短剧小程序的规范运营要求&a…

北醒单点激光雷达更改id和波特率以及Ubuntu20.04下CAN驱动

序言&#xff1a; 需要的硬件以及软件 1、USB-CAN分析仪使用顶配pro版本&#xff0c;带有支持ubuntu下的驱动包的&#xff0c;可以读取数据。 2、电源自备24V电源 3、单点激光雷达接线使用can线可以组网。 一、更改北醒单点激光雷达的id号和波特率 安装并运行USB-CAN分析仪自带…

elasticsearch8.14.1集群安装部署

elasticsearch安装部署&#xff0c;首先需要准备至少三台服务器&#xff0c;本例再windows11下安装三台vmware虚拟机&#xff0c;利用centOS7系统模拟服务器环境。 本例假设你已经安装了三台vmware和centOS7&#xff0c;且centOS7运行正常。接下来我们直接讲解elasticsearch下载…

vue实现f11全屏esc退出全屏

<template><div><p>页面内容</p><el-button type"primary" click"enter_full_screen" v-if"!full">进入</el-button><el-button type"primary" click"exitFullscreen" v-else>退…

SQL实战宝典:快速上手数据库查询与优化

文章目录 SQL 速成手册SQL 的主要功能1、基本查询语句2、表操作语句3、数据操作语句4、函数与聚合操作5、子查询与联接6、高级操作7、性能优化与安全性 基本查询语句表操作语句数据操作语句函数与聚合操作子查询与联接高级操作性能优化与安全性 SQL 速成手册 SQL&#xff08;S…

BUUCTF [WUSTCTF2020]朴实无华

首先进来不知道要干啥&#xff0c;上dirsearch扫出个机器人协议&#xff0c;一看有点东西 直接访问很明显这不是flag 主页面看他说什么不能修改头部&#xff0c;看一下数据包 发现了好东西 看到源码&#xff0c;又得绕过了。不过这编码有点问题导致乱码了 找个在线网站稍微恢复…

git代码备份

终端备份常用指令 git reflog //查看更新 git add filename //提交修改 git commit -m "20240825修改" //备注修改 git reset --hard 1094a //回退版本 exit …

【深入理解SpringCloud微服务】深入理解Eureka核心原理

深入理解Eureka核心原理 Eureka整体设计Eureka服务端启动Eureka三级缓存Eureka客户端启动 Eureka整体设计 Eureka是一个经典的注册中心&#xff0c;通过http接收客户端的服务发现和服务注册请求&#xff0c;使用内存注册表保存客户端注册上来的实例信息。 Eureka服务端接收的…

FPGA-ROM IP核的使用

1.理论 ROM全称&#xff1a;Read-Only Memory&#xff0c;也就是只读型固态半导体存储器&#xff0c;即一旦存储信息&#xff0c;无法再改变&#xff0c;信息也不会因为电源关闭消失。但在FPGA中&#xff0c;实际使用的ROM IP核并不是真正的ROM&#xff0c;其实都是内部的RAM资…

Linux之Mysql索引和优化

一、MySQL 索引 索引作为一种数据结构,其用途是用于提升数据的检索效率。 1、索引分类 - 普通索引(INDEX):索引列值可重复 - 唯一索引(UNIQUE):索引列值必须唯一,可以为NULL - 主键索引(PRIMARY KEY):索引列值必须唯一,不能为NULL,一个表只能有一个主键索引 - 全…

spring security如何适配盐存在数据库中的密码

19.token认证过滤器代码实现_哔哩哔哩_bilibili19.token认证过滤器代码实现是SpringSecurity框架教程-Spring SecurityJWT实现项目级前端分离认证授权-挑战黑马&尚硅谷的第20集视频&#xff0c;该合集共计41集&#xff0c;视频收藏或关注UP主&#xff0c;及时了解更多相关视…

C#类型基础Part1-值类型与引用类型

C#类型基础Part1-值类型与引用类型 参考资料前言值类型引用类型装箱和拆箱 参考资料 《.NET之美–.NET关键技术深入与解析》 前言 C#中的类型一共分为两类&#xff0c;一类是值类型&#xff08;Value Type&#xff09;,一类是引用类型&#xff08;Reference Type&#xff09…