GD32读写FlashGDw25Q64

GD32读写FlashGDw25Q64

1.原理图

adaf8e4b90b4414ba466ff3cbb2d642e.png

2.介绍

  1. 本实验由国产的单片机GD32F4VKT6配合国产的Flash来完成

  2. SPI0连接到Flash接口。

2.1Flash介绍

  1. 资料获取GD25Q64E-SPI NOR Flash-兆易创新 GigaDevice | 官方网站

  2. GDW25Q64,是一种高性能的串行SPI FLASH存储器,由华邦公司生产。它具有64兆位(8兆字节)的存储容量,并广泛应用于数据存储、字库存储、固件程序存储等场景。W25Q64的存储结构由块(Block)、扇区(Sector)和页(Page)组成,共有128个块,每个块包含16个扇区,每个扇区包含16个页,每页最多可存储256字节的数据。

技术规格:

存储容量:8Mb

存储单元:128块,每块16扇区,每扇区16页,每页256字节

工作电压:2.7V至3.6V

电流消耗:活跃状态下<5mA,掉电模式下<1uA

SPI模式:支持标准、双倍和四倍SPI

性能:擦写周期多达10万次,数据保存可达20年

安全性:提供软件和硬件写保护、扇区和块保护等功能

标准SPI:该GD25064E具有4个信号总线串行外设接口:串行时钟(SCLK),芯片选择(CS#),串行数据输入(SL)和串行数据输出(SO)。同时支持SPI总线模式0和3。输入数据在SCLK的上升沿锁存,数据在SCLK的下降沿移出。

双SPI:该GD25064E支持双SPI操作时,使用“双输出快速读取”和“双升/0快速读取”(3BH和BBH)命令。

  • 这些命令允许以两倍于标准SPI的速率向设备传输数据或从设备传输数据。

  • 当使用双SPI命令时Sl和SO引脚变成双向L/0引脚:100和I01。

四SPI:该GD25064E支持四SPI操作时,使用“四输出快速读取”“四通道快速读取”(6BH,EBH)命令的。

  • 这些命令允许以标准SPI的四倍速率向设备传输数据或从设备传输数据。

  • 使用QuadSPI命令时SI和S0引脚变为双向L/0引脚:10和I01,WP#和HOLD#引脚变为双向L/0引脚:102和I03。

  • 0uad SPl命令要求状态寄存器中的非易失性Quad使能位(OE)设置为1。

保持功能:当QE=0时,HOLD函数可用。如果QE=1,则HOLD功能被禁用,HOLD#引脚作为专用数据L/0引脚。

  • HOLD#信号变低以停止与设备的任何串行通信,除了写状态寄存器、编程或正在进行的擦除操作。

  • HOLD的操作需要CS#保持低电平,并从HOLD#信号的下降沿开始,SCLK信号为低电平。

  • 如果SCLK不低,HOLD操作将不会启动,直到SCLK低。HOLD条件结束于HOLD#信号的上升沿,SCLK为低。如果SCLK不低,HOLD操作将不会结束,直到SCLK低。SO是高阻抗的,SI和SCLK都不关心在HOLD操作。

  • 如果CS#在HOLD操作期间被驱动为高电平,它将重置设备的内部逻辑。要重新启动与芯片的通信,HOLD#必须在高,然后CS#必须在低。

引脚说明:

  • /CS:片选输入

  • DO(IO1):数据输出(数据输入输出1)

  • /WP(IO2):写保护输入(数据输入输出2)

  • GND:地信号

  • DI(IO0):数据输入(数据输入输出0)

  • CLK:串行时钟输入

  • /HOLD(IO3):保持输入(数据输入输出3)

  • VCC:电源

应用场景:

W25Q64适用于存储图片数据、字库数据、音频数据、保存设备运行日志文件等。其高效的连续读取模式和低功耗特性使其在各种嵌入式系统中得到广泛应用。

综上所述,W25Q64是一款性能优异、安全性高、功耗低的SPI FLASH存储器,适用于多种数据存储需求,特别是在资源受限的嵌入式系统中 1 4 。

2.2MCU介绍

  1. 采用国产的ARM架构的GD32F427VKT6

  2. GD32F4产品系列紧贴市场高端需求,以高性能、强实时、大容量特性,强化更为广泛的市场领先优势。 GD32F4系列MCU采用Arm® Cortex®-M4内核,处理器主频高达240MHz,可支持算法复杂度更高的嵌入式应用,具备更快速的实时处理能力,并拥有业界领先的大容量存储优势。 GD32F4系列具有丰富的外设资源特性,可提供多达4个USART和4个UART,3个I2C,6个SPI,2个I2S,2个CAN2.0B、1个SDIO接口、1个10/100M以太网控制器,并支持USB2.0 FS和HS通信。还配备了3个采样率高达2.6M SPS的12位高速ADC和2个12位DAC。 此外GD32F4产品系列集成了TFT LCD控制器和硬件图形加速器IPA, 以实现液晶驱动并显著提升显示图像显示画质。还支持8位至14位的Camera视频接口,便于连接数字摄像头并实现图像采集与传输。 GD32F470/F427/F425系列产品与GD32F450/F407/F405系列产品完全兼容,广泛应用于高级计算,云服务器,人工智能、工业控制、电机变频、图形显示、安防监控、传感器网络、无人机、机器人、物联网等创新领域。

  3. 资料获取:GD32F427VKT6-Arm Cortex-M4-兆易创新 GigaDevice | 官方网站

3.代码介绍

3.1 main函数

本实验就是往Flash 0地址写入数据。

int main(void)
{
    /* 初始化滴答定时器 */
    systick_config();
    /* 串口初始化 */
    AllUsartInit();
​
    /* 初始化led */
    ledInit();
    
    /* 初始化SPI */
    SPI0_Init();
​
    BSP_W25Qx_Init();//flash初始化
    BSP_W25Qx_Read_ID(ID);//读取ID
​
    printf("ID[0]=%d\n",ID[0]);
    printf("ID[1]=%d\n",ID[1]);
    
    if (BSP_W25Qx_64k_Block(0) == W25Qx_OK)//擦除一个块
    {
        printf(" Sector Erase ok\r\n");
    }
    else
    {
        printf("error");
    }
    while (1)
    {
        memset(wData1, 0, sizeof(wData1));
        memset(rData1,0,sizeof(rData1));
        //往Flash写入数据,我这儿是将数组中的100个数据写入Flash.
        参数介绍:
            1.wData2:要写入的数据地址
            2.0:Flash存储位置的地址
            3.100:存储位置的大小,
        if (BSP_W25Qx_Write(wData2, 0, 100) == W25Qx_OK) 
        {
            printf("Write0 sucess ok\n");
        } 
        else
        {
        printf("Write0 error\n");
        }
        // 读取数据从flash的零地址开始,一直读100个数据
        if (BSP_W25Qx_Read(rData2, 0, 100) == W25Qx_OK) {
            for (i = 0; i < 100; i++) {
            printf("rData2[%d] = %d ", i, rData2[i]);
        }
        }
        else
        {
            printf("Read error\n");
        }
        delay_1ms(2000);
    }
}
 

3.2 SPI初始化

/*********************************************************************************************************
 ** Function name:      SPI0_Init
 ** Descriptions:       SPI0初始化(SPI0与W25Q64 Flash连接)
 ** input parameters:   无
 ** output parameters:  无
 ** Returned value:     无
 *********************************************************************************************************/
void SPI0_Init(void)
{
    spi_parameter_struct spi_init_struct;
​
    spi_i2s_deinit(SPI0);
    
    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_SPI0);
​
    
    /* configure SPI0 GPIO */
    gpio_af_set(GPIOA, GPIO_AF_5, GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7);
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5 | GPIO_PIN_6 |        GPIO_PIN_7);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5 |       GPIO_PIN_6 | GPIO_PIN_7);
​
    /* set SPI0_NSS as GPIO*/
    gpio_af_set(GPIOA, GPIO_AF_5, GPIO_PIN_4);
    gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_4);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4);
    gpio_bit_set(GPIOA, GPIO_PIN_4);
    
​
    /* SPI0 parameter config */
    spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX;//双工模式
    spi_init_struct.device_mode          = SPI_MASTER;//作为master,提供SCLK
    spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;//8bit模式
    spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;//mode0
    spi_init_struct.nss                  = SPI_NSS_SOFT;
    spi_init_struct.prescale             = SPI_PSC_64;// 分频比
    spi_init_struct.endian               = SPI_ENDIAN_MSB;//高位在前
    spi_init(SPI0, &spi_init_struct);
​
        
    /* SPI enable */
    spi_enable(SPI0);
​
}

3.3 驱动介绍

#include "w25qxx_spi.h"
#include "systick.h"
​
​
/*Flash介绍
*8M=128块
*1块=16扇区
*1扇区=16页区
*1页区=256字节。
*/
​
​
/**
 * @brief spi数据传输函数
 * @param spi_per spi外设
 * @param byte 发送字节
 * @return 接收字节
 */
uint8_t spi_SendRcvByte(uint32_t spi_per, uint8_t byte)
{
    uint8_t data;
​
    // 等待发送缓冲区为空
    while (RESET == spi_i2s_flag_get(spi_per, SPI_FLAG_TBE))
        ;
    // 发送数据
    spi_i2s_data_transmit(spi_per, byte);
​
    // 等待发送完成
    while (SET == spi_i2s_flag_get(spi_per, SPI_FLAG_TRANS))
        ;
​
    // 等待接收缓冲区非空
    while (RESET == spi_i2s_flag_get(spi_per, SPI_FLAG_RBNE))
        ;
​
    // 接收数据
    data = spi_i2s_data_receive(spi_per);
​
    // 等待发送完成
    while (SET == spi_i2s_flag_get(spi_per, SPI_FLAG_TRANS))
        ;
​
    return data;
}
/**
 * @brief 初始化Flash芯片
 * @retval None
 */
uint8_t BSP_W25Qx_Init(void)
{
    /* Reset W25Qxxx */
    BSP_W25Qx_Reset();
​
    return BSP_W25Qx_GetStatus();
}
​
/**
 * @brief  This function reset the W25Qx.
 * @retval None
 */
static void BSP_W25Qx_Reset(void)
{
    uint8_t cmd[2] = {RESET_ENABLE_CMD, RESET_MEMORY_CMD};
    uint32_t i;
    W25Qx_Enable();
    /* Send the reset command */
​
    for (i = 0; i < 2; i++)
    {
        spi_SendRcvByte(SPI0, cmd[i]);
    }
​
    //  HAL_SPI_Transmit(&hspi1, cmd, 2, W25Qx_TIMEOUT_VALUE);
    W25Qx_Disable();
}
​
/**
 * @brief  Reads current status of the W25Q64.
 * @retval W25Q128FV memory status
 */
static uint8_t BSP_W25Qx_GetStatus(void)
{
    uint8_t cmd[] = {READ_STATUS_REG1_CMD};
    uint8_t status;
​
    W25Qx_Enable();
    /* Send the read status command */
    //  spi_write_byte(SPI0, cmd[0]);
    // HAL_SPI_Transmit(&hspi1, cmd, 1, W25Qx_TIMEOUT_VALUE);
    /* Reception of the data */
    //  status=spi_read_byte(SPI0);
    // HAL_SPI_Receive(&hspi1,&status, 1, W25Qx_TIMEOUT_VALUE);
​
    spi_SendRcvByte(SPI0, cmd[0]);
​
    status = spi_SendRcvByte(SPI0, 0x00);
​
    W25Qx_Disable();
​
    /* Check the value of the register */
    if ((status & W25Q128FV_FSR_BUSY) != 0)
    {
        return W25Qx_BUSY;
    }
    else
    {
        return W25Qx_OK;
    }
}
​
/**
 * @brief  This function send a Write Enable and wait it is effective.
 * @retval None
 */
uint8_t BSP_W25Qx_WriteEnable(void)
{
    uint8_t cmd[] = {WRITE_ENABLE_CMD};
    //  uint32_t tickstart = HAL_GetTick();
    uint32_t tickstart = 0;
    /*Select the FLASH: Chip Select low */
    W25Qx_Enable();
    /* Send the read ID command */
    //  HAL_SPI_Transmit(&hspi1, cmd, 1, W25Qx_TIMEOUT_VALUE);
    spi_SendRcvByte(SPI0, cmd[0]);
    /*Deselect the FLASH: Chip Select high */
    W25Qx_Disable();
​
    /* Wait the end of Flash writing */
    while (BSP_W25Qx_GetStatus() == W25Qx_BUSY)
    {
        tickstart++;
        delay_1ms(1);
        /* Check for the Timeout */
        if (tickstart > W25Qx_TIMEOUT_VALUE)
        {
            return W25Qx_TIMEOUT;
        }
    }
​
    return W25Qx_OK;
}
​
/**
 * @brief  Read Manufacture/Device ID.
 * @param  return value address
 * @retval None
 */
void BSP_W25Qx_Read_ID(uint8_t *ID)
{
    uint8_t cmd[4] = {READ_ID_CMD, 0x00, 0x00, 0x00};
    uint32_t i;
    W25Qx_Enable();
    /* Send the read ID command */
​
    //  HAL_SPI_Transmit(&hspi1, cmd, 4, W25Qx_TIMEOUT_VALUE);
    /* Reception of the data */
    //  HAL_SPI_Receive(&hspi1,ID, 2, W25Qx_TIMEOUT_VALUE);
​
    for (i = 0; i < 4; i++)
    {
        spi_SendRcvByte(SPI0, cmd[i]);
    }
    for (i = 0; i < 2; i++)
    {
        ID[i] = spi_SendRcvByte(SPI0, 0x00);
    }
​
    W25Qx_Disable();
}
​
/**
 * @brief  Reads an amount of data from the QSPI memory.
 * @param  pData: Pointer to data to be read
 * @param  ReadAddr: Read start address
 * @param  Size: Size of data to read
 * @retval QSPI memory status
 */
uint8_t BSP_W25Qx_Read(uint8_t *pData, uint32_t ReadAddr, uint32_t Size)
{
    uint8_t cmd[4];
    uint8_t status;
    uint32_t i;
    /* Configure the command */
    cmd[0] = READ_CMD;
    cmd[1] = (uint8_t)(ReadAddr >> 16);
    cmd[2] = (uint8_t)(ReadAddr >> 8);
    cmd[3] = (uint8_t)(ReadAddr);
​
    W25Qx_Enable();
    /* Send the read ID command */
    //  HAL_SPI_Transmit(&hspi1, cmd, 4, W25Qx_TIMEOUT_VALUE);
    for (i = 0; i < 4; i++)
        spi_SendRcvByte(SPI0, cmd[i]);
    /* Reception of the data */
    //  if (HAL_SPI_Receive(&hspi1, pData,Size,W25Qx_TIMEOUT_VALUE) != HAL_OK)
    //  {
    //    return W25Qx_ERROR;
    //  }
    for (i = 0; i < Size; i++)
        pData[i] = spi_SendRcvByte(SPI0, 0x00);
    if (status != 0x00)
    {
        return W25Qx_ERROR;
    }
    W25Qx_Disable();
    return W25Qx_OK;
}
​
/**
 * @brief  Writes an amount of data to the QSPI memory.
 * @param  pData: Pointer to data to be written
 * @param  WriteAddr: Write start address
 * @param  Size: Size of data to write,No more than 256byte.
 * @retval QSPI memory status
 */
uint8_t BSP_W25Qx_Write(uint8_t *pData, uint32_t WriteAddr, uint32_t Size)
{
    uint8_t cmd[4];
    uint32_t end_addr, current_size, current_addr;
    uint32_t tickstart = 0;
    uint32_t i;
    /* Calculation of the size between the write address and the end of the page */
    current_addr = 0;
​
    while (current_addr <= WriteAddr) // 判断地址属于哪一扇区开始
    {
        current_addr += W25Q64_PAGE_SIZE; // 0x100-> 256 bytes
    }
    current_size = current_addr - WriteAddr;
​
    /* Check if the size of the data is less than the remaining place in the page */
    if (current_size > Size)
    {
        current_size = Size;
    }
​
    /* Initialize the adress variables */ // 写入地址大小范围
    current_addr = WriteAddr;
    end_addr = WriteAddr + Size;
​
    /* Perform the write page by page */
    do
    {
        /* Configure the command */
        cmd[0] = PAGE_PROG_CMD;
        cmd[1] = (uint8_t)(current_addr >> 16);
        cmd[2] = (uint8_t)(current_addr >> 8);
        cmd[3] = (uint8_t)(current_addr);
​
        /* Enable write operations */
        BSP_W25Qx_WriteEnable();
​
        W25Qx_Enable();
        /* Send the command */
        //    if (HAL_SPI_Transmit(&hspi1,cmd, 4, W25Qx_TIMEOUT_VALUE) != HAL_OK)
        //    {
        //      return W25Qx_ERROR;
        //    }
        for (i = 0; i < 4; i++)
            spi_SendRcvByte(SPI0, cmd[i]);
​
        /* Transmission of the data */
        //    if (HAL_SPI_Transmit(&hspi1, pData,current_size, W25Qx_TIMEOUT_VALUE) != HAL_OK)
        //    {
        //      return W25Qx_ERROR;
        //    }
        for (i = 0; i < current_size; i++)
            spi_SendRcvByte(SPI0, pData[i]);
​
        W25Qx_Disable();
        /* Wait the end of Flash writing */
        while (BSP_W25Qx_GetStatus() == W25Qx_BUSY)
        {
            tickstart++;
            delay_1ms(1);
            /* Check for the Timeout */
            if (tickstart > W25Qx_TIMEOUT_VALUE)
            {
                return W25Qx_TIMEOUT;
            }
        }
​
        /* Update the address and size variables for next page programming */
        current_addr += current_size;
        pData += current_size;
        current_size = ((current_addr + W25Q64_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : W25Q64_PAGE_SIZE;
    } while (current_addr < end_addr);
​
    return W25Qx_OK;
}
​
/*
函数:读第几页数据,总页数:256*128=32768个页
pData:保存页的数据
PageNum:将数据读第几页
Size:读的数据(设计1页之内)
*/
uint8_t BSP_W25Qx_ReadPage(uint8_t *pData, uint32_t PageNum, uint32_t PageSize)
{
    return  BSP_W25Qx_Read(pData,PageNum*W25Q64_PAGE_SIZE,PageSize);
}
/*
函数:写第几页数据,总页数:256*128=32768个页
pData:写入页的数据
PageNum:将数据写入第几页
Size:写入的数据(超过一页的大小,数据会丢弃,默认最大写一页)
*/
​
uint8_t BSP_W25Qx_WriteToPage(uint8_t *pData, uint32_t PageNum, uint32_t Size)
{
     return BSP_W25Qx_Write(pData,PageNum*W25Q64_PAGE_SIZE,Size);
}
/**
* @brief  擦除扇区
 * @param  BlockAddress: Block address to erase
 * @retval QSPI memory status
 */
uint8_t BSP_W25Qx_4k_Block(uint32_t Address)
{
    uint8_t cmd[4];
    uint32_t tickstart = 0;
    uint32_t i;
    cmd[0] = SECTOR_ERASE_CMD;
    cmd[1] = (uint8_t)(Address >> 16);
    cmd[2] = (uint8_t)(Address >> 8);
    cmd[3] = (uint8_t)(Address);
​
    /* Enable write operations */
    BSP_W25Qx_WriteEnable();
​
    /*Select the FLASH: Chip Select low */
    W25Qx_Enable();
    /* Send the read ID command */
    //  HAL_SPI_Transmit(&hspi1, cmd, 4, W25Qx_TIMEOUT_VALUE);
    for (i = 0; i < 4; i++)
        spi_SendRcvByte(SPI0, cmd[i]);
    /*Deselect the FLASH: Chip Select high */
    W25Qx_Disable();
    delay_1ms(1);
    /* Wait the end of Flash writing */
    while (BSP_W25Qx_GetStatus() == W25Qx_BUSY)
    {
        tickstart++;
        delay_1ms(1);
        /* Check for the Timeout */
        if (tickstart > W25Q128FV_SECTOR_ERASE_MAX_TIME)
        {
            return W25Qx_TIMEOUT;
        }
    }
    return W25Qx_OK;
}
​
​
//擦除块
uint8_t BSP_W25Qx_64k_Block(uint32_t Address)
{
    uint8_t cmd[4];
    uint32_t tickstart = 0;
    uint32_t i;
    cmd[0] = SECTOR_Block_Erase ;
    cmd[1] = (uint8_t)(Address >> 16);
    cmd[2] = (uint8_t)(Address >> 8);
    cmd[3] = (uint8_t)(Address);
​
    /* Enable write operations */
    BSP_W25Qx_WriteEnable();
​
    /*Select the FLASH: Chip Select low */
    W25Qx_Enable();
    /* Send the read ID command */
    //  HAL_SPI_Transmit(&hspi1, cmd, 4, W25Qx_TIMEOUT_VALUE);
    for (i = 0; i < 4; i++)
        spi_SendRcvByte(SPI0, cmd[i]);
    /*Deselect the FLASH: Chip Select high */
    W25Qx_Disable();
    delay_1ms(1);
    /* Wait the end of Flash writing */
    while (BSP_W25Qx_GetStatus() == W25Qx_BUSY)
    {
        tickstart++;
        delay_1ms(1);
        /* Check for the Timeout */
        if (tickstart > W25Q128FV_SECTOR_ERASE_MAX_TIME)
        {
            return W25Qx_TIMEOUT;
        }
    }
    return W25Qx_OK;
​
}
/**
 * @brief  用于擦除GDW25Q64 Flash存储器的整个芯片。This function will take a very long time.
 * @retval QSPI memory status
 */
uint8_t BSP_W25Qx_Erase_Chip(void)
{
    uint8_t cmd[4];
    uint32_t tickstart;
    cmd[0] = SECTOR_ERASE_CMD;
​
    /* Enable write operations */
    BSP_W25Qx_WriteEnable();
​
    /*Select the FLASH: Chip Select low */
    W25Qx_Enable();
    /* Send the read ID command */
    //  HAL_SPI_Transmit(&hspi1, cmd, 1, W25Qx_TIMEOUT_VALUE);
    spi_SendRcvByte(SPI0, cmd[0]);
    /*Deselect the FLASH: Chip Select high */
    W25Qx_Disable();
​
    /* Wait the end of Flash writing */
    while (BSP_W25Qx_GetStatus() != W25Qx_BUSY)
    {
        tickstart++;
        delay_1ms(1);
        /* Check for the Timeout */
        if (tickstart > W25Q128FV_BULK_ERASE_MAX_TIME)
        {
            return W25Qx_TIMEOUT;
        }
    }
    return W25Qx_OK;
}
​

#ifndef _W25QXX_SPI_H
#define _W25QXX_SPI_H
​
/*********************************************************************************************************
*
* File                : W25Qx.h
* Hardware Environment: 
* Build Environment   : RealView MDK-ARM  Version: 5.15
* Version             : V1.0
* By                  : 
*
*                                  (c) Copyright 2005-2015, WaveShare
*                                       http://www.waveshare.net
*                                          All Rights Reserved
*
*********************************************************************************************************/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __W25Qx_H
#define __W25Qx_H
​
#ifdef __cplusplus
 extern "C" {
#endif 
​
/* Includes ------------------------------------------------------------------*/
#include "spi.h"
#include "gd32f4xx.h"
     
/** @addtogroup BSP
  * @{
  */ 
​
/** @addtogroup Components
  * @{
  */ 
  
/** @addtogroup W25Q128FV
  * @{
  */
​
/** @defgroup W25Q128FV_Exported_Types
  * @{
  */
   
/**
  * @}
  */ 
​
/** @defgroup W25Q128FV_Exported_Constants
  * @{
  */
   
/** 
  * @brief  W25Q128FV Configuration  
  */  
​
  
#define W25Q64_FLASH_SIZE                     0x800000   /* 64 MBit => 8 MBytes */
#define W25Q64_SECTOR_SIZE                    0x10000    /* 128 sectors of 64KBytes */
#define W25Q64_SUBSECTOR_SIZE                 0x1000     /* 1024 subsectors of 4kBytes */
#define W25Q64_PAGE_SIZE                      0x100      /* 16384 pages of 256 bytes */
​
#define W25Q64_DUMMY_CYCLES_READ              4
#define W25Q64_DUMMY_CYCLES_READ_QUAD         10
​
​
//超时时间:当函数读写操作超过一定时间将返回。
#define W25Q128FV_BULK_ERASE_MAX_TIME         250000
#define W25Q128FV_SECTOR_ERASE_MAX_TIME       3000
#define W25Q128FV_SUBSECTOR_ERASE_MAX_TIME    800
#define W25Qx_TIMEOUT_VALUE                   1000
​
/** 
  * @brief  W25Q128FV Commands  
  */  
/* Reset Operations */
#define RESET_ENABLE_CMD                     0x66
#define RESET_MEMORY_CMD                     0x99
​
#define ENTER_QPI_MODE_CMD                   0x38
#define EXIT_QPI_MODE_CMD                    0xFF
​
/* Identification Operations */
#define READ_ID_CMD                          0x90
#define DUAL_READ_ID_CMD                     0x92
#define QUAD_READ_ID_CMD                     0x94
#define READ_JEDEC_ID_CMD                    0x9F
​
/* Read Operations */
#define READ_CMD                             0x03
#define FAST_READ_CMD                        0x0B
#define DUAL_OUT_FAST_READ_CMD               0x3B
#define DUAL_INOUT_FAST_READ_CMD             0xBB
#define QUAD_OUT_FAST_READ_CMD               0x6B
#define QUAD_INOUT_FAST_READ_CMD             0xEB
​
/* Write Operations */
#define WRITE_ENABLE_CMD                     0x06
#define WRITE_DISABLE_CMD                    0x04
​
/* Register Operations */
#define READ_STATUS_REG1_CMD                  0x05
#define READ_STATUS_REG2_CMD                  0x35
#define READ_STATUS_REG3_CMD                  0x15
​
#define WRITE_STATUS_REG1_CMD                 0x01
#define WRITE_STATUS_REG2_CMD                 0x31
#define WRITE_STATUS_REG3_CMD                 0x11
​
​
/* Program Operations */
#define PAGE_PROG_CMD                        0x02
#define QUAD_INPUT_PAGE_PROG_CMD             0x32
​
​
/* Erase Operations */
#define SECTOR_ERASE_CMD                     0x20    //擦除扇区指令
#define SECTOR_Block_Erase                   0xD8    //擦除块指令
​
​
​
#define CHIP_ERASE_CMD                       0xC7
​
#define PROG_ERASE_RESUME_CMD                0x7A
#define PROG_ERASE_SUSPEND_CMD               0x75
​
​
/* Flag Status Register */
​
#define W25Q128FV_FSR_BUSY                    ((uint8_t)0x01)    /*!< busy */
#define W25Q128FV_FSR_WREN                    ((uint8_t)0x02)    /*!< write enable */
#define W25Q128FV_FSR_QE                      ((uint8_t)0x02)    /*!< quad enable */
​
//spi的NSS使能管脚
#define W25Qx_Enable()          gpio_bit_reset(GPIOA,GPIO_PIN_4)
#define W25Qx_Disable()         gpio_bit_set(GPIOA,GPIO_PIN_4)
​
//状态
#define W25Qx_OK            ((uint8_t)0x00)
#define W25Qx_ERROR         ((uint8_t)0x01)
#define W25Qx_BUSY          ((uint8_t)0x02)
#define W25Qx_TIMEOUT       ((uint8_t)0x03)
​
​
uint8_t BSP_W25Qx_Init(void);//初始化
static void BSP_W25Qx_Reset(void);//复位
static uint8_t BSP_W25Qx_GetStatus(void);//状态
uint8_t BSP_W25Qx_WriteEnable(void);//写使能
void BSP_W25Qx_Read_ID(uint8_t *ID);//读设备ID
uint8_t BSP_W25Qx_Read(uint8_t* pData, uint32_t ReadAddr, uint32_t Size);//读数据
uint8_t BSP_W25Qx_Write(uint8_t* pData, uint32_t WriteAddr, uint32_t Size);//写数据
uint8_t BSP_W25Qx_4k_Block(uint32_t Address);//擦除4K的空间--即擦除扇区
uint8_t BSP_W25Qx_64k_Block(uint32_t Address);//擦除64K的空间--即擦除块
uint8_t BSP_W25Qx_Erase_Chip(void);//擦除整个芯片
uint8_t BSP_W25Qx_WriteToPage(uint8_t *pData, uint32_t PageNum, uint32_t Size);//写入页
uint8_t BSP_W25Qx_ReadPage(uint8_t *pData, uint32_t PageNum, uint32_t PageSize);/*读一页*/
/**
  * @}
  */
  
/** @defgroup W25Q128FV_Exported_Functions
  * @{
  */ 
/**
  * @}
  */ 
      
/**
  * @}
  */ 
​
/**
  * @}
  */ 
​
/**
  * @}
  */
  
#ifdef __cplusplus
}
#endif
​
#endif /* __W25Qx_H */
​
​
#endif

 

 

 

  • 24
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用GD32微控制器FM25Q04的示例代码: ```c #include "gd32f10x.h" #include <stdio.h> #define SPI_CS_LOW() gpio_bit_reset(GPIOB, GPIO_PIN_12) #define SPI_CS_HIGH() gpio_bit_set(GPIOB, GPIO_PIN_12) /* 定义SPI口 */ #define SPI_PORT GPIOB #define SPI_SCK GPIO_PIN_13 #define SPI_MISO GPIO_PIN_14 #define SPI_MOSI GPIO_PIN_15 /* 定义FM25Q04指令 */ #define CMD_READ 0x03 /* 数据 */ #define CMD_WRITE 0x02 /* 数据 */ #define CMD_WREN 0x06 /* 使能 */ #define CMD_WRDI 0x04 /* 禁止 */ #define CMD_RDSR 0x05 /* 状态寄存器 */ #define CMD_WRSR 0x01 /* 状态寄存器 */ /* 定义状态寄存器位 */ #define SR_WIP (1 << 0) /* 入操作进行中 */ #define SR_WEL (1 << 1) /* 使能 */ #define SR_BP0 (1 << 2) /* 数据保护位0 */ #define SR_BP1 (1 << 3) /* 数据保护位1 */ #define SR_BP2 (1 << 4) /* 数据保护位2 */ #define SR_SRWD (1 << 7) /* 状态寄存器保护 */ /* 取状态寄存器值 */ static uint8_t fm25q04_read_status(void) { uint8_t status; SPI_CS_LOW(); /* 发送状态寄存器命令 */ spi_i2s_data_transmit(SPI0, CMD_RDSR); /* 取状态寄存器值 */ status = spi_i2s_data_receive(SPI0); SPI_CS_HIGH(); return status; } /* 等待操作完成 */ static void fm25q04_wait_for_write_complete(void) { while (fm25q04_read_status() & SR_WIP); } /* 向FM25Q04入数据 */ static void fm25q04_write_data(uint32_t address, uint8_t *data, uint32_t len) { uint32_t i; /* 使能 */ SPI_CS_LOW(); spi_i2s_data_transmit(SPI0, CMD_WREN); SPI_CS_HIGH(); /* 等待使能完成 */ fm25q04_wait_for_write_complete(); /* 入数据 */ SPI_CS_LOW(); /* 发送数据命令 */ spi_i2s_data_transmit(SPI0, CMD_WRITE); /* 发送地址 */ spi_i2s_data_transmit(SPI0, (address >> 16) & 0xFF); spi_i2s_data_transmit(SPI0, (address >> 8) & 0xFF); spi_i2s_data_transmit(SPI0, address & 0xFF); /* 发送数据 */ for (i = 0; i < len; i++) { spi_i2s_data_transmit(SPI0, data[i]); } SPI_CS_HIGH(); /* 等待操作完成 */ fm25q04_wait_for_write_complete(); } /* 从FM25Q04取数据 */ static void fm25q04_read_data(uint32_t address, uint8_t *data, uint32_t len) { uint32_t i; SPI_CS_LOW(); /* 发送数据命令 */ spi_i2s_data_transmit(SPI0, CMD_READ); /* 发送地址 */ spi_i2s_data_transmit(SPI0, (address >> 16) & 0xFF); spi_i2s_data_transmit(SPI0, (address >> 8) & 0xFF); spi_i2s_data_transmit(SPI0, address & 0xFF); /* 取数据 */ for (i = 0; i < len; i++) { data[i] = spi_i2s_data_receive(SPI0); } SPI_CS_HIGH(); } int main(void) { uint8_t data_write[] = "Hello, world!"; uint8_t data_read[32]; /* 使能GPIOB时钟 */ rcu_periph_clock_enable(RCU_GPIOB); /* 使能SPI0时钟 */ rcu_periph_clock_enable(RCU_SPI0); /* 配置SPI口 */ gpio_init(SPI_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, SPI_SCK | SPI_MOSI); gpio_init(SPI_PORT, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, SPI_MISO); /* 配置CS引脚 */ gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12); /* 配置SPI口工作模式 */ spi_parameter_struct spi_init_struct; spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; spi_init_struct.device_mode = SPI_MASTER; spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT; spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE; spi_init_struct.nss = SPI_NSS_SOFT; spi_init_struct.prescale = SPI_PSC_2; spi_init_struct.endian = SPI_ENDIAN_MSB; spi_init(SPI0, &spi_init_struct); /* 使能SPI口 */ spi_enable(SPI0); /* 向FM25Q04入数据 */ fm25q04_write_data(0x0000, data_write, sizeof(data_write)); /* 从FM25Q04取数据 */ fm25q04_read_data(0x0000, data_read, sizeof(data_read)); /* 输出取到的数据 */ printf("Read data: %s\n", data_read); while (1); return 0; } ``` 在上述代码中,使用了SPI口来与FM25Q04进行通信,通过调用fm25q04_write_data和fm25q04_read_data函数来实现向FM25Q04入数据和从FM25Q04取数据的操作。在这两个函数中,需要先发送命令和地址,然后发送数据或取数据。同时,在入数据之前需要先发送使能命令,并等待使能完成;在入或取数据完成后需要等待操作完成。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值