这里使用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(); } /******************************************************************************/