這里使用STM32的LL庫,用邏輯分析儀調了賊久,這里注意以下問題
1.查詢BUSY位,查詢一次后,必須等待2ms左右(1MS我沒試過,不需要那么快) ,否則擦除全片,擦除block等,會出現馬上返回busy=0x02的情況
2.erase后,不能馬上read ,必須有一個小等待 (10ms)左右,不然讀出來全0
3.數據寫入的時候只能按照Page來寫入,最多一次只能寫256個字節,也就是一個頁的空間,寫入的時候可以不從頁的開始地址寫入,如果一次寫入字節數溢出了一個頁的空間,那么多出來的會從循環到頁的開始地址處覆蓋原來的數據,數據手冊的10.2.14節說的很明白
/********************************** (C) COPYRIGHT ******************************* * File Name : dev_flash.h * Author : XZH * Version : V1.00 * Date : 2020/07/09 * Description : iap * * *******************************************************************************/ #ifndef __DEV_SPI_H #define __DEV_SPI_H /*******************************************************************************/ /* Include */ #include "main.h" /*******************************************************************************/ /******************************************************************************/ /* Typedef enum */ #define FLASH_WRITE_ENABLE_CMD 0x06 #define FLASH_WRITE_DISABLE_CMD 0x04 #define FLASH_READ_SR_CMD 0x05 #define FLASH_WRITE_SR_CMD 0x01 #define FLASH_READ_DATA 0x03 #define FLASH_FASTREAD_DATA 0x0b #define FLASH_WRITE_PAGE 0x02 #define FLASH_ERASE_PAGE 0x81 #define FLASH_ERASE_SECTOR 0x20 #define FLASH_ERASE_BLOCK 0xd8 #define FLASH_ERASE_CHIP 0xc7 #define FLASH_POWER_DOWN 0xb9 #define FLASH_RELEASE_POWER_DOWN 0xab #define FLASH_READ_DEVICE_ID 0x90 #define FLASH_READ_JEDEC_ID 0x9f #define DEV_FLASH_FLASH_SIZE (4*1024*1024) // 4M字節 #define DEV_FLASH_PAGE_SIZE 256 // 256 bytes #define DEV_FLASH_SECTOR_SIZE 409// 4-Kbyte #define DEV_FLASH_BLOCK_SIZE 65536 // 64-Kbyte #define FLASH_CS_LOW LL_GPIO_ResetOutputPin( GPIOA, LL_GPIO_PIN_4 ) #define FLASH_CS_HIGH LL_GPIO_SetOutputPin( GPIOA, LL_GPIO_PIN_4 ) /******************************************************************************/ /******************************************************************************/ /* Extern Variate */ /******************************************************************************/ /******************************************************************************/ /* Extern Function */ uint16_t dev_flash_read_device_id( void ); uint32_t dev_flash_read_jedec_id( void ); void dev_flash_read_bytes (uint8_t *pdata, uint32_t addr, uint16_t read_length); void dev_flash_write_page(uint8_t *pdata, uint32_t write_addr, uint16_t write_length); void dev_flash_write_bytes_nocheck(uint8_t *pdata, uint32_t write_addr, uint16_t write_length); void dev_flash_erase_page(uint32_t page_num); void dev_flash_erase_sector(uint32_t sector_num); void dev_flash_erase_block(uint32_t block_num); void dev_flash_erase_chip(void); /******************************************************************************/ #endif
/********************************** (C) COPYRIGHT ******************************* * File Name : dev_flash.c * Author : XZH * Version : V1.00 * Date : 2020/07/20 * Description : w25qxx驅動 * * *******************************************************************************/ /*******************************************************************************/ /* Include */ #include "dev_flash.h" /*******************************************************************************/ /*******************************************************************************/ /* Variables */ /*******************************************************************************/ /*******************************************************************************/ /* Globle Variate */ /******************************************************************************/ /******************************************************************************/ /* Static Function */ /******************************************************************************* * Function Name : dev_flash_delay * Description : flash驅動使用的delay * 平台適配 * * Input : delay_time 延遲時間 注意最大只能8位這里設置了 * * * Output : None * Return : None *******************************************************************************/ static void dev_flash_delay( uint8_t delay_time ) { LL_mDelay( delay_time ); } /******************************************************************************* * Function Name : dev_flash_read_write_byte * Description : 全雙工讀寫函數 平台適配 * 平台適配 * * Input : tx_data 8位數據 這里注意適配一下retry超時時間,不能太短,否則會直接跳出導致數據錯誤 * * * Output : None * Return : None *******************************************************************************/ static uint8_t dev_flash_read_write_byte(uint8_t tx_data) { uint16_t retry=0; while (LL_SPI_IsActiveFlag_TXE( SPI1 ) == RESET) { retry++; if(retry>20000)return 0; } LL_SPI_TransmitData8(SPI1,tx_data); retry=0; while (LL_SPI_IsActiveFlag_RXNE( SPI1 ) == RESET) { retry++; if(retry>20000)return 0; } return (LL_SPI_ReceiveData8(SPI1)); } /******************************************************************************* * Function Name : dev_flash_send_bytes * Description : 發送多個數據 * * * Input : pdata 數據指針 * send_length 16位數據長度 * * Output : None * Return : None *******************************************************************************/ static void dev_flash_send_bytes( uint8_t *pdata, uint16_t send_length ) { for (uint16_t i = 0; i < send_length; i++) { dev_flash_read_write_byte(pdata[i]); } } /******************************************************************************* * Function Name : dev_flash_recv_bytes * Description : spi讀取多次數據 這里發送沒用數據0xff用於給時鍾 * * * Input : recv_length 16位數據長度 * * * Output : pdata 讀取到buf的數據指針 * Return : None *******************************************************************************/ static void dev_flash_recv_bytes( uint8_t *pdata, uint16_t recv_length) { for (uint16_t i = 0; i < recv_length; i++) { *pdata ++ = dev_flash_read_write_byte( 0xff ); } } /******************************************************************************* * Function Name : dev_flash_write_enable * Description : 寫數據之前必須發寫使能 * * * Input : None * * * Output : None * Return : None *******************************************************************************/ static void dev_flash_write_enable(void) { FLASH_CS_LOW; dev_flash_read_write_byte( FLASH_WRITE_ENABLE_CMD ); FLASH_CS_HIGH; } /******************************************************************************* * Function Name : dev_flash_write_disable * Description : 寫失能 * * * Input : None * * * Output : None * Return : None *******************************************************************************/ static void dev_flash_write_disable(void) { FLASH_CS_LOW; dev_flash_read_write_byte(FLASH_WRITE_ENABLE_CMD); FLASH_CS_HIGH; } /******************************************************************************* * Function Name : dev_flash_read_sr * Description : 讀狀態寄存器 * * * Input : None * * * Output : None * Return : 8位狀態寄存器 *******************************************************************************/ static uint8_t dev_flash_read_sr(void) { uint8_t temp; FLASH_CS_LOW; dev_flash_read_write_byte( FLASH_READ_SR_CMD ); temp = dev_flash_read_write_byte( 0xff ); FLASH_CS_HIGH; return temp; } /******************************************************************************* * Function Name : dev_flash_write_sr * Description : 寫狀態寄存器 * * * Input : sr 8位狀態寄存器 * * * Output : None * Return : *******************************************************************************/ static void dev_flash_write_sr(uint8_t sr) { dev_flash_write_enable(); FLASH_CS_LOW; dev_flash_read_write_byte( FLASH_WRITE_SR_CMD ); dev_flash_read_write_byte( sr ); FLASH_CS_HIGH; //dev_flash_write_disable(); } /******************************************************************************* * Function Name : dev_flash_wait_nobusy * Description : Flash操作是否處於忙狀態,判斷之前的工作是否完成 * * * Input : None * * * Output : None * Return : 8位狀態寄存器 *******************************************************************************/ static void dev_flash_wait_nobusy(void) { while( ( (dev_flash_read_sr() ) & 0x01) == 0x01 ) dev_flash_delay(2); //必須做一下延遲 } /******************************************************************************/ /******************************************************************************/ /* Extern Function */ /******************************************************************************* * Function Name : dev_flash_read_device_id * Description : 讀取 deviceid * * * Input : None * * * Output : None * Return : 16位deviec_id 0xxx13 代表25q80這種8M的 0xxx14代表16M 0xxx15代表 32M 0xxx16代表 64M的 前面1字節看生產廠商 *******************************************************************************/ uint16_t dev_flash_read_device_id( void ) { uint16_t dat = 0; FLASH_CS_LOW; /* Send "RDID " instruction */ dev_flash_read_write_byte( FLASH_READ_DEVICE_ID ); dev_flash_read_write_byte( 0x0 ); dev_flash_read_write_byte( 0x0 ); dev_flash_read_write_byte( 0x0 ); /* Read a byte from the FLASH */ dat |= ( uint16_t )dev_flash_read_write_byte( 0xff ) << 8; /* Read a byte from the FLASH */ dat |= dev_flash_read_write_byte( 0xff ); FLASH_CS_HIGH; return( dat ); } /******************************************************************************* * Function Name : dev_flash_read_jedec_id * Description : 讀取 jedec id * * * Input : None * * * Output : None * Return : 32位jedec id 前2字節為0x00 *******************************************************************************/ uint32_t dev_flash_read_jedec_id( void ) { uint32_t dat = 0; FLASH_CS_LOW; dev_flash_read_write_byte( FLASH_READ_JEDEC_ID ); dat = ( uint32_t )dev_flash_read_write_byte( 0xff ) << 16; dat |= ( uint32_t )dev_flash_read_write_byte( 0xff ) << 8; dat |= dev_flash_read_write_byte( 0xff ); FLASH_CS_HIGH; return( dat ); } /******************************************************************************* * Function Name : dev_flash_read_bytes * Description : 讀取數據 * * * Input : addr 讀取地址 * read_length 讀取長度 * * Output : pdata 讀取到的buf指針 * Return : None *******************************************************************************/ void dev_flash_read_bytes (uint8_t *pdata, uint32_t addr, uint16_t read_length) { uint8_t temp_buff[3] = {0}; temp_buff[0] = (uint8_t)(addr >> 16); temp_buff[1] = (uint8_t)(addr >> 8); temp_buff[2] = (uint8_t)(addr >> 0); FLASH_CS_LOW; dev_flash_read_write_byte(FLASH_READ_DATA); dev_flash_read_write_byte( temp_buff[0] ); dev_flash_read_write_byte( temp_buff[1] ); dev_flash_read_write_byte( temp_buff[2] ); dev_flash_recv_bytes( pdata, read_length ); FLASH_CS_HIGH; } /******************************************************************************* * Function Name : dev_flash_write_page * Description : 寫一頁 最多256字節 注意這里別越扇區了!!!不會自己檢查 * * * Input : pdata 數據 * addr 寫入地址 * write_length 寫入長度 * Output : None * Return : None *******************************************************************************/ void dev_flash_write_page(uint8_t *pdata, uint32_t write_addr, uint16_t write_length) { dev_flash_write_enable(); FLASH_CS_LOW; dev_flash_read_write_byte(FLASH_WRITE_PAGE); dev_flash_read_write_byte((uint8_t)(write_addr>>16)); dev_flash_read_write_byte((uint8_t)(write_addr>>8)); dev_flash_read_write_byte((uint8_t)(write_addr>>0)); dev_flash_send_bytes( pdata, write_length); FLASH_CS_HIGH; dev_flash_wait_nobusy(); //dev_flash_write_disable(); } /******************************************************************************* * Function Name : dev_flash_write_bytes_nocheck * Description : 寫數據 自動查詢當前地址 自動越扇區寫入 注意這里只能寫擦除過的 * * * Input : pdata 數據 * write_addr 寫入地址 * write_length 寫入長度 * Output : None * Return : None *******************************************************************************/ void dev_flash_write_bytes_nocheck(uint8_t *pdata, uint32_t write_addr, uint16_t write_length) { uint16_t PageByte = 256 - write_addr % 256; if(write_length <= PageByte) { PageByte = write_length; } while(1) { dev_flash_write_page(pdata, write_addr, PageByte); if(write_length == PageByte) break; else { pdata += PageByte; write_addr += PageByte; write_length -= PageByte; if(write_length > 256) { PageByte = 256; } else { PageByte = write_length; } } } } /******************************************************************************* * Function Name : dev_flash_erase_page * Description : 擦除一頁 256字節 * * * Input : page_num 頁碼 注意填入的是 地址/256 * * * Output : None * Return : None *******************************************************************************/ void dev_flash_erase_page(uint32_t page_num) { page_num *= 256; dev_flash_write_enable(); FLASH_CS_LOW; dev_flash_read_write_byte(FLASH_ERASE_PAGE); dev_flash_read_write_byte((uint8_t)(page_num>>16)); dev_flash_read_write_byte((uint8_t)(page_num>>8)); dev_flash_read_write_byte((uint8_t)(page_num>>0)); FLASH_CS_HIGH; dev_flash_wait_nobusy(); //dev_flash_write_disable(); } /******************************************************************************* * Function Name : dev_flash_erase_sector * Description : 擦除一扇區 4K字節 * * * Input : sector_num 扇區碼 注意填入的是 地址/4096 * * * Output : None * Return : None *******************************************************************************/ void dev_flash_erase_sector(uint32_t sector_num) { sector_num *= 4096; dev_flash_write_enable(); dev_flash_wait_nobusy(); FLASH_CS_LOW; dev_flash_read_write_byte( FLASH_ERASE_SECTOR); dev_flash_read_write_byte( (uint8_t)( sector_num >> 16) ); dev_flash_read_write_byte( (uint8_t)( sector_num >> 8) ); dev_flash_read_write_byte( (uint8_t) sector_num ); FLASH_CS_HIGH; dev_flash_wait_nobusy(); //dev_flash_write_disable(); } /******************************************************************************* * Function Name : dev_flash_erase_sector * Description : 擦除一塊 64K字節 * * * Input : sector_num 扇區碼 注意填入的是 地址/65536 * * * Output : None * Return : None *******************************************************************************/ void dev_flash_erase_block(uint32_t block_num) { block_num *= 65536; dev_flash_write_enable(); dev_flash_wait_nobusy(); FLASH_CS_LOW; dev_flash_read_write_byte( FLASH_ERASE_BLOCK ); dev_flash_read_write_byte( (uint8_t)(block_num >> 16) ); dev_flash_read_write_byte( (uint8_t)(block_num >> 8) ); dev_flash_read_write_byte( (uint8_t)(uint8_t)(block_num >> 0) ); FLASH_CS_HIGH; dev_flash_wait_nobusy(); //dev_flash_write_disable(); } /******************************************************************************* * Function Name : dev_flash_erase_chip * Description : 整片擦除 * * * Input : None * * * Output : None * Return : None *******************************************************************************/ void dev_flash_erase_chip(void) { dev_flash_write_enable(); FLASH_CS_LOW; dev_flash_read_write_byte(FLASH_ERASE_CHIP); FLASH_CS_HIGH; dev_flash_wait_nobusy(); //dev_flash_write_disable(); } /******************************************************************************/