STM32 SPI读取写入W25Q64JVSSIQ

w25q64.h

c 复制代码
#ifndef __W25Q64_H
#define __W25Q64_H

#include "main.h"
#include "spi.h"

// 引脚定义
#define W25Q64_CS_PIN        GPIO_PIN_15
#define W25Q64_CS_PORT       GPIOA

// W25Q64指令集
#define W25Q64_WRITE_ENABLE  0x06
#define W25Q64_WRITE_DISABLE 0x04
#define W25Q64_READ_STATUS1  0x05
#define W25Q64_WRITE_STATUS1 0x01
#define W25Q64_READ_DATA     0x03
#define W25Q64_PAGE_PROGRAM  0x02
#define W25Q64_SECTOR_ERASE  0x20
#define W25Q64_CHIP_ERASE    0xC7
#define W25Q64_POWER_DOWN    0xB9
#define W25Q64_RELEASE_PD    0xAB
#define W25Q64_MANUF_DEVICE_ID 0x90
#define W25Q64_JEDEC_ID      0x9F

// Flash容量参数
#define W25Q64_PAGE_SIZE     256
#define W25Q64_SECTOR_SIZE   4096
#define W25Q64_TOTAL_SIZE    (8 * 1024 * 1024)  // 8MB

// 函数声明
void W25Q64_Init(void);
uint8_t W25Q64_ReadByte(uint32_t addr);
void W25Q64_ReadBytes(uint32_t addr, uint8_t *buf, uint32_t len);
void W25Q64_WriteByte(uint32_t addr, uint8_t data);
void W25Q64_WriteBytes(uint32_t addr, uint8_t *data, uint32_t len);
void W25Q64_ModifyByte(uint32_t addr, uint8_t data);
void W25Q64_EraseSector(uint32_t sector_addr);
void W25Q64_WaitBusy(void);
uint32_t W25Q64_ReadID(void);

#endif /* __W25Q64_H */

w25q64.c

c 复制代码
#include "w25q64.h"

// 私有函数声明
static void W25Q64_CS_Low(void);
static void W25Q64_CS_High(void);
static void W25Q64_WriteEnable(void);
static uint8_t W25Q64_ReadStatus(void);

// 初始化函数
void W25Q64_Init(void)
{
    W25Q64_CS_High();
    HAL_Delay(10);
    
    // 读取ID验证连接
    uint32_t id = W25Q64_ReadID();
    if(id == 0xEF4017)  // W25Q64JV的JEDEC ID
    {
        // 初始化成功
    }
}

// 读取单个字节
uint8_t W25Q64_ReadByte(uint32_t addr)
{
    uint8_t cmd[4] = {W25Q64_READ_DATA, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF};
    uint8_t data = 0;
    
    W25Q64_CS_Low();
    HAL_SPI_Transmit(&hspi6, cmd, 4, 100);
    HAL_SPI_Receive(&hspi6, &data, 1, 100);
    W25Q64_CS_High();
    
    return data;
}

// 读取多个字节
void W25Q64_ReadBytes(uint32_t addr, uint8_t *buf, uint32_t len)
{
    uint8_t cmd[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();
}

// 写入单个字节
void W25Q64_WriteByte(uint32_t addr, uint8_t data)
{
    uint8_t cmd[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();
}

// 写入多个字节(注意:不能跨页写入)
void W25Q64_WriteBytes(uint32_t addr, uint8_t *data, uint32_t len)
{
    // 确保不跨页写入
    uint32_t page_remain = W25Q64_PAGE_SIZE - (addr % W25Q64_PAGE_SIZE);
    uint32_t write_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);
    }
}

// 修改指定地址的字节(先擦除后写入)
void W25Q64_ModifyByte(uint32_t addr, uint8_t data)
{
    // 计算所在扇区
    uint32_t sector_addr = addr - (addr % W25Q64_SECTOR_SIZE);
    uint8_t sector_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_t i = 0; i < W25Q64_SECTOR_SIZE; i += W25Q64_PAGE_SIZE)
    {
        W25Q64_WriteBytes(sector_addr + i, &sector_buffer[i], W25Q64_PAGE_SIZE);
    }
}

// 擦除扇区(4KB)
void W25Q64_EraseSector(uint32_t sector_addr)
{
    uint8_t cmd[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操作完成
void W25Q64_WaitBusy(void)
{
    while(W25Q64_ReadStatus() & 0x01)
    {
        HAL_Delay(1);
    }
}

// 读取JEDEC ID
uint32_t W25Q64_ReadID(void)
{
    uint8_t cmd = W25Q64_JEDEC_ID;
    uint8_t id_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];
}

// 私有函数实现
static void W25Q64_CS_Low(void)
{
    HAL_GPIO_WritePin(W25Q64_CS_PORT, W25Q64_CS_PIN, GPIO_PIN_RESET);
}

static void W25Q64_CS_High(void)
{
    HAL_GPIO_WritePin(W25Q64_CS_PORT, W25Q64_CS_PIN, GPIO_PIN_SET);
}

static void W25Q64_WriteEnable(void)
{
    uint8_t cmd = W25Q64_WRITE_ENABLE;
    
    W25Q64_CS_Low();
    HAL_SPI_Transmit(&hspi6, &cmd, 1, 100);
    W25Q64_CS_High();
}

static uint8_t W25Q64_ReadStatus(void)
{
    uint8_t cmd = W25Q64_READ_STATUS1;
    uint8_t status;
    
    W25Q64_CS_Low();
    HAL_SPI_Transmit(&hspi6, &cmd, 1, 100);
    HAL_SPI_Receive(&hspi6, &status, 1, 100);
    W25Q64_CS_High();
    
    return status;
}

使用例程

c 复制代码
#include "w25q64.h"

// 测试函数
void main(void)
{
    uint8_t read_data;
    uint8_t write_data = 0xAA;
    uint32_t test_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_t write_buffer[256];
    uint8_t read_buffer[256];
    
    for(int i = 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配置要点

  1. SPI6配置:
    · Mode: Full-Duplex Master
    · Hardware NSS Signal: Disable
    · Prescaler: 根据HCLK频率调整(建议≤50MHz)
  2. GPIO配置:
    · PA15: GPIO_Output(CS引脚)
    · PB3: SPI6_SCK
    · PB4: SPI6_MISO
    · PB5: SPI6_MOSI
  3. 时钟配置:
    · 确保SPI时钟不超过W25Q64的最大频率(104MHz)

注意事项

  1. 写操作前必须擦除:Flash只能从1变为0,所以写前需要擦除(变为0xFF)
  2. 擦除最小单位:4KB扇区
  3. 写操作跨页处理:页大小为256字节,跨页时需要分页写入
  4. 修改函数效率:W25Q64_ModifyByte会擦除整个扇区,频繁修改时应考虑缓存机制

这个驱动提供了基本的读写和修改功能,可以根据需要扩展更多功能(如整片擦除、快速读取等)。

相关推荐
fie88891 天前
基于51单片机的航模遥控器6通道接收机程序
单片机·嵌入式硬件·51单片机
bu_shuo1 天前
嵌入式硬件工程师VS单板硬件工程师
嵌入式硬件·电子工程师·单板硬件
llilian_161 天前
选择北斗导航卫星信号模拟器注意事项总结 北斗导航卫星模拟器 北斗导航信号模拟器
功能测试·单片机·嵌入式硬件·测试工具·51单片机·硬件工程
Yyq130208696821 天前
MH2457,‌国产 32 位屏驱 MCU‌芯片,支持‌1080P 高清显示‌与‌以太网通信‌,广泛应用于两轮车仪表盘及工控屏等领域
单片机·嵌入式硬件
爱分享的阿Q1 天前
STM32现代化AI开发环境搭建:从Keil到VSCode+AI的范式转移
人工智能·vscode·stm32
爱吃程序猿的喵1 天前
南邮计科电工电子实验B《RLC串联谐振电路》实验报告
单片机·嵌入式硬件
独小乐1 天前
009.中断实践之实现按键测试|千篇笔记实现嵌入式全栈/裸机篇
linux·c语言·驱动开发·笔记·嵌入式硬件·arm
XINVRY-FPGA1 天前
XC7VX690T-2FFG1157I Xilinx AMD Virtex-7 FPGA
arm开发·人工智能·嵌入式硬件·深度学习·fpga开发·硬件工程·fpga
bubiyoushang8881 天前
利用STM32实现Modbus通信(RTU从机方案)
stm32·单片机·嵌入式硬件
cmpxr_1 天前
【单片机】常用设计模式
单片机·嵌入式硬件·设计模式