• GD32F30x_读写外部FLASH(GD25Q256DF)驱动程序


    一、工具

      1、硬件:GD32F30x系列单片机

      2、编译环境:KEIL

      3、Flash芯片:GD25Q256DF

    二、芯片介绍

      GD25Q256DF是一款256M-bit(32Mbyte)的串行Flash,使用的是SPI通讯。该芯片的页大小、扇区大小及其详细信息如下表所示:

       其它详细信息请阅读数据手册,这里不再赘述。

    三、SPI驱动程序

      SPI驱动程序使用的是硬件SPI方式实现的。

      1、SPI引脚配置

    #define SPI_CS_HIGH          {GPIO_BOP(GPIOB) = (uint32_t)GPIO_PIN_12;}
    #define SPI_CS_LOW           {GPIO_BC(GPIOB) = (uint32_t)GPIO_PIN_12;}
    /* 
     *@brief spi引脚配置
     *@retval none
     *@author Mr.W
     *@date 2020-8-4
     */
    static void bsp_spi1_gpio_cfg(void)
    {
        rcu_periph_clock_enable(RCU_GPIOB);
        rcu_periph_clock_enable(RCU_AF);
        
        /* PB12 as NSS */
        gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12);    
        
        /* SPI1 GPIO config: SCK/PB13, MISO/PB14, MOSI/PB15 */
        gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13 | GPIO_PIN_15);
        gpio_init(GPIOB, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_14);
        
        SPI_CS_HIGH;
    }

       2、SPI配置

    /* 
     *@brief spi配置
     *@retval none
     *@author Mr.W
     *@date 2020-8-4
     */
    static void bsp_spi1_cfg(void)
    {
        spi_parameter_struct spi_init_struct;
        
        rcu_periph_clock_enable(RCU_SPI1);
        
        /* SPI1 parameter config */
        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_32;
        spi_init_struct.endian               = SPI_ENDIAN_MSB;
        spi_init(SPI1, &spi_init_struct);
        
        spi_enable(SPI1);
        SPI_CS_LOW;
    }

      3、SPI初始化

    /* 
     *@brief spi初始化
     *@retval none
     *@author Mr.W
     *@date 2020-8-4
     */
    void bsp_spi1_init(void)
    {
        bsp_spi1_gpio_cfg();
        bsp_spi1_cfg();
    }

      4、SPI读写

    /* 
     *@brief spi读写
     *@param data 要发送的数据
     *@param timeout 超时时长
     *@retval 接收到的数据或者超时值0xFF
     *@author Mr.W
     *@date 2020-8-4
     */
    uint8_t bsp_spi1_transmit_receive_data(uint8_t data, uint32_t timeout)
    {
        while(RESET == spi_i2s_flag_get(SPI1, SPI_FLAG_TBE))
        {
            if(timeout-- == 0)
                return 0xFF;        
        }
        spi_i2s_data_transmit(SPI1, data);
        while(RESET == spi_i2s_flag_get(SPI1, SPI_FLAG_RBNE))
        {
            if(timeout-- == 0)
                return 0xFF;        
        }
        return spi_i2s_data_receive(SPI1);
    }

    四、GD25Q256DF驱动程序

       1、分别封装读写函数

    /* 
     *@brief 读一个字节数据
     *@retval 读到的数据
     *@author Mr.W
     *@date 2020-8-4
     */
    static uint8_t gd25q256df_read_byte(void)
    {
        return bsp_spi1_transmit_receive_data(0xA5, 0xFFFFFFFF);
    }
    
    /* 
     *@brief 写一个字节数据
     *@param 要写的数据
     *@retval none
     *@author Mr.W
     *@date 2020-8-4
     */
    static void gd25q256df_write_byte(uint8_t data)
    {
        bsp_spi1_transmit_receive_data(data, 0xFFFFFFFF);
    }

       2、写使能和写禁止

    /* 
     *@brief Write Enable
     *@retval none
     *@author Mr.W
     *@date 2020-8-4
     */
    static void gd25q256df_write_enable(void)
    {
        SPI_CS_LOW;
        /* 发送写使能命令 */
        gd25q256df_write_byte(0x06);
        SPI_CS_HIGH;
    }
    
    /* 
     *@brief Write Disable
     *@retval none
     *@author Mr.W
     *@date 2020-8-4
     */
    void gd25q256df_write_disable(void)
    {
        SPI_CS_LOW;
        /* 发送写失能命令 */
        gd25q256df_write_byte(0x04);
        SPI_CS_HIGH;
    }

      3、读单个寄存器

    /* 
     *@brief 读单个状态寄存器
     *@param 指定寄存器命令
     *@retval none
     *@author Mr.W
     *@date 2020-8-4
     */
    static uint8_t gd25q256df_read_single_status_register(uint8_t command)
    {
        uint8_t status = 0;
        
        SPI_CS_LOW;
        gd25q256df_write_byte(command&0xFF);
        status = gd25q256df_read_byte();
        SPI_CS_HIGH;
        
        return status;
    }

      4、等待写结束

    /* 
     *@brief 等写结束;编程、擦除和写状态寄存器后均可使用该函数
     *@retval none
     *@author Mr.W
     *@date 2020-8-4
     */
    static uint8_t gd25q256df_wait_write_end(void)
    {
        uint8_t status = 0;
        uint32_t timeout = 0;
        
        SPI_CS_LOW;
        /* 发送读状态寄存器命令 */
        gd25q256df_write_byte(0x05);
        do
        {
            status = gd25q256df_read_byte();
            timeout++;
            if(timeout > GD25Q256DF_WAIT_MAX_TIME)
                return 0;    
        }while(status & 0x01);
        SPI_CS_HIGH;
        
        return 1;
    }

      5、写状态寄存器

    /* 
     *@brief 写状态寄存器
     *@param command 指定寄存器命令
     *@paran status 写入寄存器的状态值
     *@retval none
     *@author Mr.W
     *@date 2020-8-4
     */
    void gd25q256df_write_status_register(uint8_t command, uint8_t status)
    {
        SPI_CS_LOW;
        gd25q256df_write_byte(command&0xFF);
        gd25q256df_write_byte(status&0xFF);
        SPI_CS_HIGH;
    }

      6、复位

    /* 
     *@brief 复位gd25q256
     *@retval none
     *@author Mr.W
     *@date 2020-8-4
     */
    static uint8_t gd25q256df_reset(void)
    {
        uint8_t status = 0;
        uint32_t timeout = 0;
            
        SPI_CS_LOW;
        /* Enable Reset (66H) */
        gd25q256df_write_byte(0x66);
        SPI_CS_HIGH;
        
        SPI_CS_LOW;
        /* Reset (99H) */
        gd25q256df_write_byte(0x99);
        SPI_CS_HIGH;
        
        do{
            /* Read Status Register-1 (05H) */
            status = gd25q256df_read_single_status_register(0x05);
            timeout++;
            if(timeout > GD25Q256DF_WAIT_MAX_TIME)
                return 0;
        }while(status == 0x01);
        
        return 1;
    }

      7、写页,每一页256字节

    /* 
     *@brief 写页
     *@param pdata 数据起始地址
     *@param addr 写到存储空间的起始地址
     *@param size 写入数据大小
     *@retval none
     *@author Mr.W
     *@date 2020-8-4
     */
    static uint8_t gd25q256df_write_page(const uint8_t* pdata, uint32_t addr, uint16_t size)
    {
        uint8_t ret = 0;
            
        /* 使能写 */
        gd25q256df_write_enable();
        
        SPI_CS_LOW;
        
        /* 发送写命令 */
        gd25q256df_write_byte(0x02);
        /* 发送32位地址 */
        gd25q256df_write_byte((addr & 0xFF000000) >> 24);
        gd25q256df_write_byte((addr & 0xFF0000) >> 16);
        gd25q256df_write_byte((addr & 0xFF00) >> 8);
        gd25q256df_write_byte(addr & 0xFF);
        
        while(size--)
        {
            gd25q256df_write_byte(*pdata);
            pdata++;
        }
        
        SPI_CS_HIGH;
        /* 等待写完成 */
        ret = gd25q256df_wait_write_end();    
        
        return ret;
    }

      8、写扇区,每一个扇区4096字节

    /* 
     *@brief 写扇区
     *@param pdata 数据起始地址
     *@param addr 写到存储空间的起始地址
     *@param size 写入数据大小
     *@retval none
     *@author Mr.W
     *@date 2020-8-4
     */
    uint8_t gd25q256df_write_sector(const uint8_t* pdata, uint32_t addr, uint16_t size)
    {
        uint8_t ret = 0;
        uint16_t page_offset = 0;
        uint16_t page_remain = 0;
    
        /* 计算页内偏移地址 */
        page_offset = addr%256;
        /* 计算页内剩余空间 */
        page_remain = 256 - page_offset;
        
        if(size <= page_remain){
            page_remain = size;
        }
        
        while(1)
        {
            ret = gd25q256df_write_page(pdata, addr, page_remain);
            if(page_remain != size){
                addr += page_remain;
                pdata += page_remain;
                size -= page_remain;
                if(size > 256){
                    page_remain = 256;
                }
                else{
                    page_remain = size;
                }
            }else{
                break;
            }
        }
        return ret;
    }

      9、初始化,这里要注意因为使用的芯片存储空间较大,需设置为4字节地址模式

    /* 
     *@brief gd25q256初始化
     *@retval none
     *@author Mr.W
     *@date 2020-8-4
     */
    uint8_t gd25q256df_init(void)
    {
        uint8_t ret = 0;
        uint8_t reg_status = 0;
        
        ret = gd25q256df_reset();
        /* 读状态寄存器2 */
        reg_status = gd25q256df_read_single_status_register(0x35);
        if((reg_status&0x01) == 0)
        {
            SPI_CS_LOW;
            /* Enter 4-Byte Address Mode (B7H) */
            gd25q256df_write_byte(0xB7);
            SPI_CS_HIGH;
        }
        
        return ret;
    }

      10、读ID,可以验证SPI操作正常与否

    /* 
     *@brief 读ID
     *@retval ID号(0xC84019)
     *@author Mr.W
     *@date 2020-8-4
     */
    uint32_t gd25q256df_read_id(void)
    {
        uint8_t id1, id2, id3;
        uint32_t uiID;
        
        SPI_CS_LOW;
        
        /* 发送读ID命令 */
        gd25q256df_write_byte(0x9F);
        
        id1 = gd25q256df_read_byte();
        id2 = gd25q256df_read_byte();
        id3 = gd25q256df_read_byte();
        
        SPI_CS_HIGH;
        
        uiID = (id1 << 16) | (id2 << 8) | id3;
        
        return uiID;
    }

      11、读数据

    /* 
     *@brief 从存储器中读数据
     *@param pdata 读到的数据起始地址
     *@param address 要读数据存放的起始地址
     *@param size 读取的数据大小
     *@retval none
     *@author Mr.W
     *@date 2020-8-4
     */
    void gd25q256df_read_data(uint8_t* pdata, uint32_t address, uint16_t size)
    {
        uint32_t i;
        
        SPI_CS_LOW;
        
        /* 发送读命令 */
        gd25q256df_write_byte(0x03);
        /* 发送32位地址 */
        gd25q256df_write_byte(address >> 24);
        gd25q256df_write_byte(address >> 16);
        gd25q256df_write_byte(address >> 8);
        gd25q256df_write_byte(address);
        /* 开始接收数据 */
        for(i = 0; i < size; i++)
        {
            pdata[i] = gd25q256df_read_byte();
        }
    
        SPI_CS_HIGH;
    }

      12、写数据

    /* 扇区缓冲区 */
    uint8_t gd25q256_buffer[4096];
    /* 
     *@brief 向存储器中写数据
     *@param pdata 要写数据的起始地址
     *@param address 要写数据存放的起始地址
     *@param size 要写数据大小
     *@retval none
     *@author Mr.W
     *@date 2020-8-4
     */
    uint8_t gd25q256df_write_data(const uint8_t* pdata, uint32_t address, uint16_t size)
    {
        uint8_t ret = 0;    
        uint32_t sector_pos = 0;
        uint16_t sector_offset = 0;            
        uint16_t sector_remain = 0;
        uint32_t i;
        
        /* 扇区地址 */
        sector_pos = address/4096;
        /* 计算扇区内地址偏移 */
        sector_offset = address%4096;
        /* 计算扇区内剩余空间 */
        sector_remain = 4096 - sector_offset;
        
        if(size <= sector_remain){
            sector_remain = size;
        }
        while(1)
        {
            /* 读当前扇区的所有数据 */
            gd25q256df_read_data(gd25q256_buffer, sector_pos*4096, 4096);
            for(i = 0; i < sector_remain; i++){
                if(gd25q256_buffer[sector_offset + i] != 0xFF)
                    break;
            }
            if(i < sector_remain){
                /* 擦除当前扇区 */
                gd25q256df_sector_erase(sector_pos*4096);
                for(i = 0; i < sector_remain; i++){
                    gd25q256_buffer[sector_offset + i] = pdata[i];
                }
                ret = gd25q256df_write_sector(gd25q256_buffer, sector_pos*4096, 4096);
            }else{
                ret = gd25q256df_write_sector(pdata, address, sector_remain);
            }
            
            if(size == sector_remain){
                break;
            }else{
                sector_pos++;
                sector_offset = 0;
                
                pdata += sector_remain;
                address += sector_remain;
                size -= sector_remain;
                if(size > 4096){
                    sector_remain = 4096;
                }else{
                    sector_remain = size;
                }
            }
        }
        
        return ret;
    }

      13、扇区擦除

    /* 
     *@brief 扇区擦除
        Any address inside the sector is a valid address for the 4k Sector Erase (SE) command.
     *@param sector_addr 扇区地址
     *@retval none
     *@author Mr.W
     *@date 2020-8-4
     */
    uint8_t gd25q256df_sector_erase(uint32_t sector_addr)
    {
        uint8_t ret = 0;
            
        /* 写使能 */
        gd25q256df_write_enable();
        
        ret = gd25q256df_wait_write_end();
        if(ret == 0)
            return ret;
        
        SPI_CS_LOW;
        
        /* 发送读命令 */
        gd25q256df_write_byte(0x20);
        /* 发送32位地址 */
        gd25q256df_write_byte((sector_addr & 0xFF000000) >> 24);
        gd25q256df_write_byte((sector_addr & 0xFF0000) >> 16);
        gd25q256df_write_byte((sector_addr & 0xFF00) >> 8);
        gd25q256df_write_byte(sector_addr & 0xFF);
        
        SPI_CS_HIGH;
        
        /* 等待擦除完成 */
        ret = gd25q256df_wait_write_end();
        
        return ret;
    }

      14、整片擦除

    /* 
     *@brief 整片擦除
     *@retval none
     *@author Mr.W
     *@date 2020-8-4
     */
    uint8_t gd25q256df_chip_erase(void)
    {
        uint8_t ret = 0;
        
        /* 写使能 */
        gd25q256df_write_enable();
    
        SPI_CS_LOW;
        /* 发送擦除命令 */
        gd25q256df_write_byte(0xC7);
    
        SPI_CS_HIGH;    
        
        /* 等待擦除完成 */
        ret = gd25q256df_wait_write_end();
        
        return ret;
    }

    #endif

      

  • 相关阅读:
    java线程池实践
    JAVA中间件(middleware)模式
    [开源]制作docker镜像不依赖linux和Docker环境
    利用浏览器favicon的缓存机制(F-Cache)生成客户端浏览器唯一指纹
    Docker镜像构建原理解析(不装docker也能构建镜像)
    ORM框架对分表分库之分库和分表指定不同的字段
    volatile的内存屏障的坑
    go和python的比较,获取当前时间是今年第几个星期
    c++学生管理系统
    c++学生管理系统(三)
  • 原文地址:https://www.cnblogs.com/wenhao-Web/p/14052266.html
Copyright © 2020-2023  润新知