移植SFUD,驱动SPI FLASH ZD25WQ80

1、关于SFUD

SFUD (Serial Flash Universal Driver) 串行 Flash 通用驱动库,支持众多spi flash,关于SFUD的详细资料可参考:https://github.com/armink/SFUD。

2、为什么会有通用驱动

JEDEC (固态技术协会)针对串行 Flash 功能的参数表制定了统一标准,https://www.jedec.org/standards-documents/docs/jesd216b,最新版 V1.6B 。该标准规定了,每个 Flash 中会存在一个参数表,该表中会存放 Flash 容量、写粒度、擦除命令、地址模式等 Flash 规格参数。目前,除了部分厂家旧款 Flash 型号会不支持该标准,其他绝大多数新出厂的 Flash 均已支持 SFDP 标准。所以该库在初始化时会优先读取 SFDP 表参数。这也正是SFUD驱动的由来。
对应用来说只需要配置好spi,就可以读写flash了,因为SFUD提供了这些标准驱动。

3、关于SPI Flash ZD25WQ80

翻看ZD25WQ80的手册,可以发现ZD25WQ80是符合JEDEC的标准的,因此可以可以直接使用SFUD 这个驱动。
在这里插入图片描述
在这里插入图片描述

4、代码移植

4.1、SFUD代码结构

在这里插入图片描述
SFUD的代码结构也比较简单:
./demo里面是示例,
./docs里面是文档说明,
./sfud里面是源码,
我们移植也只关心./sfud文件夹里面的代码:
./sfud/inc是头文件,裁剪配置在这里面,
./sfud/port是接口相关文件,
./sfud/src是核心源代码。

4.2、配置

#define SFUD_DEBUG_MODE //调试
#define SFUD_USING_SFDP //使用SFDP标准参数表
// #define SFUD_USING_FLASH_INFO_TABLE //flash信息表,(flash不支持SFDP时才用到)
// #define SFUD_USING_QSPI //支持QSPI
定义所使用的spi flash名称:

enum {SFUD_XXXX_DEVICE_INDEX = 0,
};#define SFUD_FLASH_DEVICE_TABLE                                                \
{                                                                              \[SFUD_XXXX_DEVICE_INDEX] = {.name = "ZD25WQ80", .spi.name = "SPI2"},           \
}

4.3、SPI驱动

配置完成后,就需要编写spi底层驱动了,这里涉及到两个函数sfud_spi_port_initspi_write_read,分别是spi初始化和spi读写。
针对我们使用的MM32L0136C7P我们完成这两个函数如下:

void SpiInit(void)
{/* Enable GPIO clock for SPIy and SPIz */RCC_EnableAHBPeriphs(RCC_AHB_PERIPH_GPIOB, true);/*  SPI2 :  PB12/CS  PB13/SCK    PB14/MISO   PB15/MOSI    *//* SPI2. */RCC_EnableAPB1Periphs(RCC_APB1_PERIPH_SPI2, true);RCC_ResetAPB1Periphs(RCC_APB1_PERIPH_SPI2);GPIO_Init_Type gpio_init;/* PB12 - SPI2_CS. */gpio_init.Pins  = GPIO_PIN_12;gpio_init.PinMode  = GPIO_PinMode_Out_PushPull;gpio_init.Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &gpio_init);/* SPI2 GPIO configuration: SCK/PB13, MISO/PB14, MOSI/PB15 *//* Configure SPIy pins: SCK, MISO and MOSI ---------------------------------*//* PB13 - SPI2_SCK. */gpio_init.Pins  = GPIO_PIN_13;gpio_init.PinMode  = GPIO_PinMode_AF_PushPull;gpio_init.Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &gpio_init);GPIO_PinAFConf(GPIOB, gpio_init.Pins, GPIO_AF_0);/* PB14 - SPI2_MISO. */gpio_init.Pins  = GPIO_PIN_14;gpio_init.PinMode  = GPIO_PinMode_In_Floating;gpio_init.Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &gpio_init);GPIO_PinAFConf(GPIOB, gpio_init.Pins, GPIO_AF_0);/* PB15 - SPI2_MOSI. */gpio_init.Pins  = GPIO_PIN_15;gpio_init.PinMode  = GPIO_PinMode_AF_PushPull;gpio_init.Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &gpio_init);GPIO_PinAFConf(GPIOB, gpio_init.Pins, GPIO_AF_0);/* Setup SPI Master. */SPI_Master_Init_Type spi_master_init;spi_master_init.ClockFreqHz = 24000000u;spi_master_init.BaudRate = 1000000u;spi_master_init.XferMode = SPI_XferMode_TxRx;spi_master_init.PolPha = SPI_PolPha_Alt0;spi_master_init.DataWidth = SPI_DataWidth_8b;spi_master_init.LSB = false;spi_master_init.AutoCS = false; /* if set false, need SPI_EnableCS after spi enable. */SPI_InitMaster(SPI2, &spi_master_init);/* Enable SPI Master. */SPI_Enable(SPI2, true);
}
/*** 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);}GPIO_ClearBits(GPIOB, GPIO_PIN_12);/* 开始读写数据 */for (size_t i = 0, retry_times; i < write_size + read_size; i++) {/* 先写缓冲区中的数据到 SPI 总线,数据写完后,再写 dummy(0xFF) 到 SPI 总线 */if (i < write_size) {send_data = *write_buf++;} else {send_data = SFUD_DUMMY_DATA;}/* 发送数据 */retry_times = 1000;while ( SPI_STATUS_TX_FULL & SPI_GetStatus(SPI2) ){SFUD_RETRY_PROCESS(NULL, retry_times, result);}if (result != SFUD_SUCCESS) {goto exit;}SPI_PutData(SPI2, send_data);/* 接收数据 */retry_times = 1000;while (0u == (SPI_STATUS_RX_DONE & SPI_GetStatus(SPI2)) )		{SFUD_RETRY_PROCESS(NULL, retry_times, result);}if (result != SFUD_SUCCESS) {goto exit;}read_data = SPI_GetData(SPI2);/* 写缓冲区中的数据发完后,再读取 SPI 总线中的数据到读缓冲区 */if (i >= write_size) {*read_buf++ = read_data;}}exit:GPIO_SetBits(GPIOB, GPIO_PIN_12);return result;
}static void spi_lock(const sfud_spi *spi) 
{	
}static void spi_unlock(const sfud_spi *spi) 
{
}/* about 100 microsecond delay */
static void retry_delay_100us(void) 
{uint32_t delay = 120;while(delay--);
}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*/SpiInit();flash->spi.wr = spi_write_read; //Requiredflash->spi.lock = spi_lock;flash->spi.unlock = spi_unlock;flash->retry.delay = retry_delay_100us;flash->retry.times = 10000; //Requiredreturn result;
}

上面还涉及到了延时以及锁的问题,这里我们实际测试后,锁可以不用处理。

4.4、测试

#define SFUD_DEMO_TEST_BUFFER_SIZE                     1024static uint8_t sfud_demo_test_buf[SFUD_DEMO_TEST_BUFFER_SIZE];
/*** 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);}
}void SFUDTest(void)
{if(sfud_init() == SFUD_SUCCESS) {sfud_demo(0, sizeof(sfud_demo_test_buf), sfud_demo_test_buf);}
}

5、现象

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
将读出来关键信息,与手册对比可以发现是一致的。

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

stm32hal库应用笔记之 硬件I2C读写ZD24C64

话不多说 直接上资料 这是ZD24C64 数据手册里面的读写操作时序。 数据手册网上有很多相信大家也能随便就找到。 如下图所示&#xff1a; 这是使用iic按字节写操作 器件地址&#xff1a;我的是0xA0 来不及解释了赶快上车&#xff1a; 在下使用的是STM32_CubeIDE,个人觉得还是…

ZD680无人机组装调试流程

在权盛电子店里买了ZD680无人机套装&#xff0c;记录拼装调试的大体流程。 下面部分操作说明来源于权盛电子提供的教程。 一、配置清单 机架&#xff1a;ZD680&#xff0c;轴距680mm&#xff0c;机臂直径20mm&#xff0c;材质碳纤&#xff0c;载重3kg 电机&#xff1a;致盈…

麒麟系统在FT2000+下预留连续物理内存空间

1、背景介绍 项目需要在系统下预留一段连续物理地址空间供FPGA启动DMA直接写入&#xff0c;这样提高读写带宽。目前有两种方式可以实现该需求。 注意&#xff1a;前提是操作系统将内存空间访问权限全部放开&#xff0c;否则无法预留空间。 2、实现方法 方式一&#xff1a; …

适用于vue3中图片全屏展示

App中的代码片段 <style scoped> .container {background: url("./assets/back2.jpg");height: 100%;width: 100%;position: fixed;background-size: 100% 100%; } </style>

单机部署多个 nginx

为方便测试&#xff0c;单机部署多个 nginx&#xff0c;监听不同端口&#xff0c;响应不同页面。以其中一个为例&#xff1a; 1、新建目录&#xff1a; /root/nginx/8571/ /root/nginx/8571/html/ /root/nginx/8571/log/ 2、将文件&#xff1a;/usr/sbin/nginx 拷贝至 /root/n…

Ubuntu系统设置开机自启

在测试国产操作系统&#xff1a;银河麒麟、UOS统信机器的过程中&#xff0c;发现开机不自启&#xff0c;总结以下几种方式实现自启 一.rc.local rc.local脚本是一个Ubuntu开机后自动执行的脚本&#xff0c;可以在脚本内添加行指令&#xff0c;该脚本位于/etc/路径下&#xff…

linux系统的服务器怎么设置上电自启,Linux系统开机自启流程

第一步:POST(Power On Safe Test)加电自检 当按下电源键以后,CPU因为有电流通过便开始对自身、I/O设备、内存等硬件进行检验。那么CPU如何得知这些自检指令?我们知道指令存在于内存当中,寻找内存也需要指令。这看起来似乎是一个“死结”。如何解决呢?在CPU生厂商生产CPU的…

Nginx服务器启停命令

Nginx服务器启停命令 nginx安装好后&#xff0c;维护和更新&#xff0c;经常使用到启动和停止nginx服务。现主要介绍两种方式&#xff1a; 1、nginx服务器的信号控制 nginx中的master 和worker 进程是什么&#xff1f;nginx的工作方式是什么&#xff1f;如何获取进程的PID?信…

REDIS如何开启多个库,而不是增加其实例

(转&#xff1a;)来自运维生存时间: http://www.ttlsa.com/html/4010.html 一台服务器上都快开启200个redis实例了&#xff0c;看着就崩溃了。这么做无非就是想让不同类型的数据属于不同的应用程序而彼此分开。 那么&#xff0c;redis有没有什么方法使不同的应用程序数据彼此分…

IDEA同一个工程启动多个实例

spring cloud 集群中同一个工程需要在多个不同的端口启动多个实例&#xff0c;怎么实现呢 下面我已一个简单的服务提供者、服务消费者、eureka注册中心小Demo说明 &#xff08;之所以一个窗口显示3个项目是因为我先创建了一个空project&#xff0c;之后在空project的基础上ne…

易语言启动线程传递多个类型的数据变量

易语言的启动线程默认是传递一个整数的&#xff0c;在 易语言启动线程传递文本参数中&#xff0c;我们讲解了传递文本参数&#xff0c;今天&#xff0c;我们示范传递多个变量 思路是我们把要传递的变量定义成一个整数型数组&#xff0c;传递的是参数内容的内存地址指针&#xf…

在IDEA启动多个Spring Boot工程实例

对于配置比较多方便保存的则使用下面的方式&#xff1a; 方式一 spring boot允通过命名约定按照一定的格式(application-{profile}.properties)来定义多个配置文件&#xff0c;然后通过在application.properyies通过spring.profiles.active来具体激活一个或者多个配置文件&am…

JMeter压力测试-多线程组并发测试(多场景混合并发)

JMeter压力测试&#xff08;一&#xff09;中设置线程组的线程数&#xff0c;即执行时的并发用户数&#xff0c;适用于单个线程组的并发测试。 如果测试需求出现如下场景&#xff0c;该如何使用JMeter实现压力测试呢&#xff1f;即JMeter如何实现多线程组并发测试&#xff0c;…

Java同时执行多个线程(优雅的进行线程池创建)

欢迎访问我的个人博客:www.ifueen.com 有时候我们会遇到这样的场景&#xff0c;需要通过多线程同时去对某些信息进行处理然后再进行开展后续的业务&#xff0c;这时候就需要用到多线程来同时并发处理这些数据 其实多线程处理的方式有很多&#xff0c;线程池&#xff0c;自定义…

python3-启动多个线程

问题&#xff1a;在for循环里面想同时启动多个线程&#xff0c;但是程序运行过程中产生的是线程一个一个启动&#xff0c;即&#xff1a;启动第一个线程之后主线程等待子线程运行完成之后才启动第二个&#xff0c;代码如下&#xff1a; import threading import time def thre…

【优化】Maven开启多线程构建编译 性能优化

【优化】Maven开启多线程构建编译 性能优化 提高编译的效率 IDEA配置 增加跳过测试代码的编译命令&#xff1a; -Dmaven.test.skiptrue 指明多线程进行编译&#xff1a;-Dmaven.compile.forktrue mvn -T 4 clean install mvn -T 1C clean install 以本机为例 使用记事本打…

HTML总结 2[转]

以下转载和参考自&#xff1a;HTML 表单。 1、表格 可以通过 CSS 设置表格的样式&#xff1a; 如下为将上面table.lamp th,td样式中的padding注释掉&#xff0c;开启table.lamp中的padding的效果&#xff1a; 2、列表 3、表单 表单用于收集用户的输入&#xff0c;如下图所…

因为懒得下载Visual Studio,而使用dotnet-cli这件事

window常用命令: 1.windows清屏指令:CLS 2.创建文件夹: mkdir 3.使用敲过的命令:↑ 或者 ↓(这个方向键) nuget官方地址:https://www.nuget.org/ 一、概述: .NET 命令行接口 (CLI) 工具是用于开发、生成、运行和发布 .NET 应用程序的跨平台工具链。 .NET CLI 附带…

10个非常有用的Python库,你知道几个?

整理&#xff5c;TesterHome 这里给大家介绍10个不是最流行但非常有用的Python库&#xff0c;希望可以提供参考帮助。 PyO3 PyO3是一个Rust库&#xff0c;可以让你在Rust中编写Python模块。它可以利用 Rust 的速度和安全性编写高性能的 Python 模块。 https://github.com/PyO3…