使用GD32F470的硬件SPI读写W25Q64

server/2025/2/10 20:53:56/

代码简单改下引脚定义便可以使用!

使用的单片机具体型号:GD32F470ZGT6

简单介绍下W25Q64:

/* W25Q64 性能参数 */
/* 容量:8MByte = 64Mbit */
/* 有128个块,每个块有64KByte */
/* 每个块有16个扇区,每个扇区有4KByte */
/* 每个扇区有16页,每个页有256Byte */
/* 最小擦除单位:扇区:4KByte */
/* 最大写入单位:页:256Byte */

 w25q64.c:

#include "bsp_w25q64.h"
#include "bsp_usart.h"
#include "systick.h"#define uchar   uint8_t
#define ushort  uint16_t
#define ulong   uint32_t/*********************************************************** 函 数 名 称:w25q64_init_config* 函 数 功 能:w25q64初始化* 传 入 参 数:无* 函 数 返 回:无* 作       者:LCKFB* 备       注:无
**********************************************************/
void w25q64_init_config(void)
{//SPI参数定义结构体spi_parameter_struct spi_init_struct;rcu_periph_clock_enable(W25Q64_SPI_CLK_RCU);  // 使用F端口rcu_periph_clock_enable(W25Q64_SPI_MISO_RCU);  // 使用F端口rcu_periph_clock_enable(W25Q64_SPI_MOSI_RCU);  // 使用F端口rcu_periph_clock_enable(W25Q64_SPI_RCU);     // 使能SPI4//引脚复用gpio_af_set(W25Q64_SPI_CLK_PORT, W25Q64_SPI_AF, W25Q64_SPI_CLK_PIN);gpio_af_set(W25Q64_SPI_MISO_PORT, W25Q64_SPI_AF, W25Q64_SPI_MISO_PIN);gpio_af_set(W25Q64_SPI_MOSI_PORT, W25Q64_SPI_AF, W25Q64_SPI_MOSI_PIN);//引脚模式gpio_mode_set(W25Q64_SPI_CLK_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, W25Q64_SPI_CLK_PIN);gpio_mode_set(W25Q64_SPI_MISO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, W25Q64_SPI_MISO_PIN);gpio_mode_set(W25Q64_SPI_MOSI_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, W25Q64_SPI_MOSI_PIN);//输出模式gpio_output_options_set(W25Q64_SPI_CLK_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, W25Q64_SPI_CLK_PIN);gpio_output_options_set(W25Q64_SPI_MISO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, W25Q64_SPI_MISO_PIN);gpio_output_options_set(W25Q64_SPI_MOSI_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, W25Q64_SPI_MOSI_PIN);//开启CS引脚时钟rcu_periph_clock_enable(W25Q64_SPI_CS_RCU);//配置CS引脚模式gpio_mode_set(W25Q64_SPI_CS_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, W25Q64_SPI_CS_PIN);//配置CS输出模式gpio_output_options_set(W25Q64_SPI_CS_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, W25Q64_SPI_CS_PIN);//W25Q64不选中gpio_bit_write(W25Q64_SPI_CS_PORT, W25Q64_SPI_CS_PIN, 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_LOW_PH_1EDGE;		//极性相位,模式3(1,1)spi_init_struct.nss                  = SPI_NSS_SOFT;              	//软件csspi_init_struct.prescale             = SPI_PSC_32;                 	//SPI时钟预分频为32(SPI通信速度有讲究,如果太快,会导致波形和预期现象都出现偏差)spi_init_struct.endian               = SPI_ENDIAN_MSB;            	//高位在前//将参数填入SPI4spi_init(W25Q64_SPIx, &spi_init_struct);//使能SPIspi_enable(W25Q64_SPIx);}static uchar spi_read_write_byte(uchar dat)
{//等待发送缓冲区为空while(RESET == spi_i2s_flag_get(W25Q64_SPIx,  SPI_FLAG_TBE) );spi_i2s_data_transmit(W25Q64_SPIx, dat);//等待接收缓冲区为空while(RESET == spi_i2s_flag_get(W25Q64_SPIx,  SPI_FLAG_RBNE) );return spi_i2s_data_receive(W25Q64_SPIx);
}ushort W25Q64_readID(void)
{ushort  temp = 0;W25Q64_CS(0);										// 片选W25Q64spi_read_write_byte(W25Q64_Manufacturer_Device_ID);//发送读取ID命令spi_read_write_byte(0x00);spi_read_write_byte(0x00);spi_read_write_byte(0x00);//接收数据temp |= spi_read_write_byte(0xFF) << 8;               // 读取 manufacturer ID,固定为EFtemp |= spi_read_write_byte(0xFF);                    // 读取 device IDW25Q64_CS(1);										// 取消片选return temp;
}static void W25Q64_write_enable(void)
{W25Q64_CS(0);spi_read_write_byte(W25Q64_Write_Enable);				// 发送写使能指令W25Q64_CS(1);
}/*********************************************************** 函 数 名 称:W25Q64_wait_busy* 函 数 功 能:判断W25Q64是否忙* 传 入 参 数:无* 函 数 返 回:无* 作       者:LCKFB* 备       注:无
**********************************************************/
void W25Q64_wait_busy(void)
{unsigned char byte = 0;do{W25Q64_CS(0);spi_read_write_byte(W25Q64_Read_Status_register_1);byte = spi_read_write_byte(0Xff);W25Q64_CS(1);}while( ( byte & 0x01 ) == 1 );          // 如果得到的数据最低位是1,则等待
}/*********************************************************** 函 数 名 称:W25Q64_erase_sector* 函 数 功 能:擦除一个扇区* 传 入 参 数:addr=擦除的扇区号* 函 数 返 回:无* 作       者:LCKFB* 备       注:addr=擦除的扇区号,范围=0~15
**********************************************************/
void W25Q64_erase_sector(ulong addr)
{addr *= 4096;W25Q64_write_enable();  //写使能W25Q64_wait_busy();     //判断忙W25Q64_CS(0);spi_read_write_byte(W25Q64_Sector_Erase_4KB);spi_read_write_byte((uchar)((addr) >> 16));spi_read_write_byte((uchar)((addr) >> 8));spi_read_write_byte((uchar)addr);W25Q64_CS(1);
}// /**********************************************************
//  * 函 数 名 称:W25Q64_write
//  * 函 数 功 能:写数据到W25Q64进行保存
//  * 传 入 参 数:buffer=写入的数据内容	addr=写入地址	numbyte=写入数据的长度
//  * 函 数 返 回:无
//  * 作       者:LCKFB
//  * 备       注:无
// **********************************************************/
// void W25Q64_write(uchar *buffer, ulong addr, ushort numbyte)
// {
//     unsigned int i = 0;
//     W25Q64_erase_sector(addr / 4096); //擦除扇区数据,2^12=4096//     //等待擦除完成
//     W25Q64_wait_busy();//     delay_ms(50);
//     W25Q64_write_enable();//写使能
//     W25Q64_wait_busy(); //忙检测
//     //写入数据
//     W25Q64_CS(0);
//     spi_read_write_byte(W25Q64_Page_Program);
//     spi_read_write_byte((uchar)((addr) >> 16));
//     spi_read_write_byte((uchar)((addr) >> 8));
//     spi_read_write_byte((uchar)addr);
//     for(i = 0; i < numbyte; i++)
//     {
//         spi_read_write_byte(buffer[i]);
//     }
//     W25Q64_CS(1);
// }// /**********************************************************
//  * 函 数 名 称:W25Q64_read
//  * 函 数 功 能:读取W25Q64的数据
//  * 传 入 参 数:buffer=读出数据的保存地址  read_addr=读取地址		read_length=读去长度
//  * 函 数 返 回:无
//  * 作       者:LCKFB
//  * 备       注:无
// **********************************************************/
// void W25Q64_read(uchar *buffer, ulong read_addr, ushort read_length)
// {
//     ushort i;
//     //等待擦除完成
//     W25Q64_wait_busy();//     W25Q64_CS(0);
//     spi_read_write_byte(W25Q64_Read_Data);
//     spi_read_write_byte((uchar)((read_addr) >> 16));
//     spi_read_write_byte((uchar)((read_addr) >> 8));
//     spi_read_write_byte((uchar)read_addr);
//     for(i = 0; i < read_length; i++)
//     {
//         buffer[i] = spi_read_write_byte(0XFF);
//     }
//     W25Q64_CS(1);
// }/* 写入数据 */
/* buffer   : 字符数组 */
/* section  : 扇区,0-2047 */
/* page     : 页数,0-15 */
/* addr     : 具体地址,0-255 */
/* numbyte  : 写入多少个数据,0-255 */
uchar W25Q64_write(uchar *buffer, ushort section, uchar page, ushort addr, ushort numbyte)
{// 输入地址越界if((section < 0) || (section > 2047) || (page > 15) || (page < 0) || (addr >255) || (addr < 0)){return 1;}// 输入字节数超过每页的数量,会造成数据覆盖if(addr + numbyte > 255){return 2;}ulong write_addr = (section * 4096) + (page * 256) + addr;W25Q64_erase_sector(section);   //擦除扇区数据,2^12=4096//等待擦除完成W25Q64_wait_busy();delay_ms(50);W25Q64_write_enable();          //写使能W25Q64_wait_busy();             //忙检测//写入数据W25Q64_CS(0);spi_read_write_byte(W25Q64_Page_Program);spi_read_write_byte((uchar)((write_addr) >> 16));spi_read_write_byte((uchar)((write_addr) >> 8));spi_read_write_byte((uchar)write_addr);for(ushort i = 0; i < numbyte; i++){spi_read_write_byte(buffer[i]);}W25Q64_CS(1);return 0;
}/* 写入数据 */
/* buffer   : 字符数组 */
/* section  : 扇区,0-2047 */
/* page     : 页数,0-15 */
/* addr     : 具体地址,0-255 */
/* numbyte  : 写入多少个数据,0-255 */
uchar W25Q64_read(uchar *buffer, ushort section, uchar page, ushort addr, ushort numbyte)
{// 输入地址越界if((section < 0) || (section > 2047) || (page > 15) || (page < 0) || (addr >255) || (addr < 0)){return 1;}// 输入字节数超过每页的数量,会造成数据错误if(addr + numbyte > 255){return 2;}ulong read_addr = (section * 4096) + (page * 256) + addr;//等待擦除完成W25Q64_wait_busy();W25Q64_CS(0);spi_read_write_byte(W25Q64_Read_Data);spi_read_write_byte((uchar)((read_addr) >> 16));spi_read_write_byte((uchar)((read_addr) >> 8));spi_read_write_byte((uchar)read_addr);for(ushort i = 0; i < numbyte; i++){buffer[i] = spi_read_write_byte(0XFF);}W25Q64_CS(1);return 0;
}

w25q64.h:

#ifndef _BSP_W25Q64_H__
#define _BSP_W25Q64_H__#include "gd32f4xx.h"
#include "gd32f470_config.h"/* W25Q64 性能参数 */
/* 容量:8MByte = 64Mbit */
/* 有128个块,每个块有64KByte */
/* 每个块有16个扇区,每个扇区有4KByte */
/* 每个扇区有16页,每个页有256Byte */
/* 最小擦除单位:扇区:4KByte */
/* 最大写入单位:页:256Byte *///W25Q64指令表1
#define W25Q64_Write_Enable						          0x06
#define W25Q64_Write_Disable                              0x04
#define W25Q64_Read_Status_register_1				      0x05
#define W25Q64_Read_Status_register_2				      0x35
#define W25Q64_Write_Status_register				      0x01
#define W25Q64_Page_Program							      0x02
#define W25Q64_Quad_Page_Program				          0x32
#define W25Q64_Block_Erase_64KB						      0xD8
#define W25Q64_Block_Erase_32KB						      0x52
#define W25Q64_Sector_Erase_4KB						      0x20
#define W25Q64_Chip_Erase							      0xC7
#define W25Q64_Erase_Suspend					          0x75
#define W25Q64_Erase_Resume							      0x7A
#define W25Q64_Power_down							      0xB9
#define W25Q64_High_Performance_Mode				      0xA3
#define W25Q64_Continuous_Read_Mode_Reset			      0xFF
#define W25Q64_Release_Power_Down_HPM_Device_ID		      0xAB
#define W25Q64_Manufacturer_Device_ID				      0x90
#define W25Q64_Read_Uuique_ID						      0x4B
#define W25Q64_JEDEC_ID								      0x9F//W25Q64指令集表2(读指令)
#define W25Q64_Read_Data						          0x03
#define W25Q64_Fast_Read						          0x0B
#define W25Q64_Fast_Read_Dual_Output				      0x3B
#define W25Q64_Fast_Read_Dual_IO					      0xBB
#define W25Q64_Fast_Read_Quad_Output				      0x6B
#define W25Q64_Fast_Read_Quad_IO					      0xEB
#define W25Q64_Octal_Word_Read_Quad_IO				      0xE3#define W25Q64_SPIx                	SPI4
#define W25Q64_SPI_AF              	GPIO_AF_5#define W25Q64_SPI_RCU             	RCU_SPI4
#define W25Q64_SPI_CLK_RCU         	RCU_GPIOF
#define W25Q64_SPI_MISO_RCU        	RCU_GPIOF
#define W25Q64_SPI_MOSI_RCU        	RCU_GPIOF
#define W25Q64_SPI_CS_RCU        	RCU_GPIOF#define W25Q64_SPI_CLK_PORT 	    GPIOF			    // LED GPIOB的端口
#define W25Q64_SPI_CLK_PIN 	    	GPIO_PIN_7          // LED GPIOB的引脚#define W25Q64_SPI_MISO_PORT 	    GPIOF			    // LED GPIOB的端口  单片机的输入,接Flash的DO
#define W25Q64_SPI_MISO_PIN 	    GPIO_PIN_8          // LED GPIOB的引脚#define W25Q64_SPI_MOSI_PORT 	    GPIOF			    // LED GPIOB的端口  单片机的输出,接Flash的DI
#define W25Q64_SPI_MOSI_PIN 	    GPIO_PIN_9          // LED GPIOB的引脚#define W25Q64_SPI_CS_PORT 	    	GPIOF			    // LED GPIOB的端口
#define W25Q64_SPI_CS_PIN 	    	GPIO_PIN_6          // LED GPIOB的引脚#define W25Q64_CS(state) ((state) ? gpio_bit_set(W25Q64_SPI_CS_PORT, W25Q64_SPI_CS_PIN) : gpio_bit_reset(W25Q64_SPI_CS_PORT, W25Q64_SPI_CS_PIN))void w25q64_init_config(void);
ushort W25Q64_readID(void);uchar W25Q64_write(uchar *buffer, ushort section, uchar page, ushort addr, ushort numbyte);
uchar W25Q64_read(uchar *buffer, ushort section, uchar page, ushort addr, ushort numbyte);#endif

mian.c测试:

int main(void)
{systick_config();				// 滴答定时器 初始化nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);  // 优先级分组(0-4, 0-4)Test_UART_Init(115200);			// 调试的 UART 初始化// w25q64_init_config();// uchar buff[4] = {0x00, 0x11, 0xFF, 0x80};// if(W25Q64_write(buff, 0, 0, 0, 4))// {//     printf("write error\n");// }// uchar rec_buff[4];// if(W25Q64_read(rec_buff, 0, 0, 0, 4))// {//     printf("read error\n");// }// printf("rec_buff: %x %x %x %x\n",rec_buff[0],rec_buff[1],rec_buff[2],rec_buff[3]);while(1){}
}

        有一个地方需要注意一下,就是判断W25Q64忙不忙,这里采用死等的办法,没有做超时溢出,可能会卡死,并且Flash扇区擦除是非常耗时间的操作,几十个ms去死等,显然不靠谱,最好搞个定时器搞个标志位去查看,这里只是测试就懒得做那么多了。

        另外这种flash,只能从1变0,反之是不行的,所以在写数据前,必须擦除所写的扇区的所有数据。

        图片的现象,跟我给的代码实际现象不一样,大家复制改定义就行。


http://www.ppmy.cn/server/166577.html

相关文章

【高级架构师】多线程和高并发编程(三):锁(中)深入ReentrantLock

文章目录 3、深入ReentrantLock3.1 ReentrantLock和synchronized的区别3.2 AQS概述3.3 加锁流程源码剖析3.3.1 加锁流程概述3.3.2 三种加锁源码分析3.3.2.1 lock方法3.3.2.2 tryLock方法3.3.2.3 lockInterruptibly方法 3.4 释放锁流程源码剖析3.4.1 释放锁流程概述3.4.2 释放锁…

详解Nginx no live upstreams while connecting to upstream

网上看到几个相关的文章&#xff0c;觉得很不错&#xff0c;这里整理记录分享一下&#xff0c;供大家参考。 upstream配置分 在分析问题原因之前&#xff0c;我们先来看下关于上面upstream配置一些相关的参数配置说明&#xff0c;参考下面表格 ngx_http_proxy_module 这里重…

作业作业作业

优化登录框&#xff1a; 当用户点击取消按钮&#xff0c;弹出问题对话框&#xff0c;询问是否要确定退出登录&#xff0c;并提供两个按钮&#xff0c;yes|No&#xff0c;如果用户点击的Yes&#xff0c;则关闭对话框&#xff0c;如果用户点击的No&#xff0c;则继续登录 当用户…

如何在Android Studio中开发一个简单的Android应用?

Android Studio是开发Android应用的官方集成开发环境&#xff08;IDE&#xff09;&#xff0c;它提供了许多强大的功能&#xff0c;使得开发者能够高效地创建Android应用。如果你是Android开发的初学者&#xff0c;本文将引导你如何在Android Studio中开发一个简单的Android应用…

MySql --- 作业

一. 触发器 1建立两个表:goods(商品表)、orders(订单表) mysql> create database mydb16_tigger; Query OK, 1 row affected (0.01 sec)mysql> use mydb16_tigger; Database changed mysql> mysql> CREATE TABLE goods (-> gid CHAR(8) PRIMARY KEY,->…

视频采集卡接口

采集卡的正面有MIC IN、LINE IN以及AUDIO OUT三个接口&#xff0c; MIC IN为麦克风输入&#xff0c;我们如果要给采集到的视频实时配音或者是在直播的时候进行讲解&#xff0c;就可以在这里插入一个麦克风&#xff0c; LINE IN为音频线路输入&#xff0c;可以外接播放背景音乐…

Java语言的区块链

Java语言的区块链&#xff1a;深入探索与实践 在当今数字化迅猛发展的时代&#xff0c;区块链技术正逐渐成为各个行业变革的核心力量。作为一种分布式账本技术&#xff0c;区块链以其去中心化、透明性和安全性等特点&#xff0c;彻底改变了我们对数据存储和事务处理的传统认知…

Docker 和 Docker Compose

Docker 和 Docker Compose 是两个相关但用途不同的工具&#xff0c;它们在容器化应用的管理和部署中扮演不同的角色。以下是它们的核心区别&#xff1a; 1. 功能定位 Docker: 是一个容器化平台&#xff0c;用于创建、运行和管理单个容器。适用于单个容器应用的开发和测试。通过…