1. 程式人生 > 實用技巧 >基於GD32F30x微控制器_讀寫外部FLASH(GD25Q256DF)驅動程式

基於GD32F30x微控制器_讀寫外部FLASH(GD25Q256DF)驅動程式

一、工具

  1、硬體:GD32F30x系列微控制器

  2、編譯環境:KEIL

  3、Flash晶片:GD25Q256DF

二、晶片介紹

  GD25Q256DF是一款256M-bit的序列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、初始化

/* 
 *@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