基於GD32F30x微控制器_讀寫外部FLASH(GD25Q256DF)驅動程式
阿新 • • 發佈:2020-11-28
一、工具
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