Flash
bsp_spi_1.h
#ifndef __BSP_SPI_1_H #define __BSP_SPI_1_H #include "stm32f4xx.h" #define SPI_1_MODE 0 //0:硬件I2C 1:软件I2C #define SPI_1_DelayMS SYSTICK_DelayMS #if SPI_1_MODE == 0 //硬件SPI #define SPI_1 SPI5 #define SPI_1_CLK RCC_APB2Periph_SPI5 #define SPI_1_CLOCKCMD RCC_APB2PeriphClockCmd //SCK引脚 #define SPI_1_SCK_GPIO_PORT GPIOF #define SPI_1_SCK_GPIO_PIN GPIO_Pin_7 #define SPI_1_SCK_GPIO_PinSource GPIO_PinSource7 #define SPI_1_SCK_GPIO_AF GPIO_AF_SPI5 //MISO引脚 #define SPI_1_MISO_GPIO_PORT GPIOF #define SPI_1_MISO_GPIO_PIN GPIO_Pin_8 #define SPI_1_MISO_GPIO_PinSource GPIO_PinSource8 #define SPI_1_MISO_GPIO_AF GPIO_AF_SPI5 //MOSI引脚 #define SPI_1_MOSI_GPIO_PORT GPIOF #define SPI_1_MOSI_GPIO_PIN GPIO_Pin_9 #define SPI_1_MOSI_GPIO_PinSource GPIO_PinSource9 #define SPI_1_MOSI_GPIO_AF GPIO_AF_SPI5 //NSS(CS)引脚 片选选普通GPIO即可 #define SPI_1_NSS1_GPIO_PORT GPIOF #define SPI_1_NSS1_GPIO_PIN GPIO_Pin_6 #define SPI_1_GPIO_CLK RCC_AHB1Periph_GPIOF #define SPI_1_GPIO_CLOCKCMD RCC_AHB1PeriphClockCmd void SPI_1_ConfigInit(void); #elif SPI_1_MODE == 1 //软件SPI #define CPOL 1 //时钟极性 0:SCK空闲时间处于低电平 1:SCK空闲时间处于高电平 #define CPHA 1 //时钟相位 0:奇数时钟边沿采样 1:偶数时钟边沿采样 //SCK引脚 #define SPI_1_SCK_GPIO_PORT GPIOF #define SPI_1_SCK_GPIO_PIN GPIO_Pin_7 //MISO引脚---主设备输入,从设备输出 #define SPI_1_MISO_GPIO_PORT GPIOF #define SPI_1_MISO_GPIO_PIN GPIO_Pin_8 //MOSI引脚---主设备输出,从设备输入 #define SPI_1_MOSI_GPIO_PORT GPIOF #define SPI_1_MOSI_GPIO_PIN GPIO_Pin_9 //NSS(CS)引脚 片选选普通GPIO即可 #define SPI_1_NSS1_GPIO_PORT GPIOF #define SPI_1_NSS1_GPIO_PIN GPIO_Pin_6 #define SPI_1_GPIO_CLK RCC_AHB1Periph_GPIOF #define SPI_1_GPIO_CLOCKCMD RCC_AHB1PeriphClockCmd void SPI_1_ConfigInit(void); uint32_t SPI_1_ReadByte(uint8_t Slave_Addr,uint8_t Read_Addr,uint8_t RAM_Size); void SPI_1_WriteByte(uint8_t Slave_Addr,uint8_t Write_Addr,uint32_t Write_Data,uint8_t RAM_Size); void SPI_1_ReadBuffer(uint8_t Slave_Addr,uint8_t Read_Addr,uint8_t Read_Num,uint8_t* p_Read_Buffer); void SPI_1_WriteBuffer(uint8_t Slave_Addr,uint8_t Write_Addr,uint8_t Write_Num,uint8_t* p_Write_Buffer); #endif /******************************************************************************** 使用标准的固件库控制IO *******************************************************************************/ #define SPI_1_SCK_L() GPIO_ResetBits(SPI_1_SCK_GPIO_PORT, SPI_1_SCK_GPIO_PIN) #define SPI_1_MISO_L() GPIO_ResetBits(SPI_1_MISO_GPIO_PORT, SPI_1_MISO_GPIO_PIN) #define SPI_1_MOSI_L() GPIO_ResetBits(SPI_1_MOSI_GPIO_PORT, SPI_1_MOSI_GPIO_PIN) #define SPI_1_NSS_L(n) if(n == 1) \ { \ GPIO_ResetBits(SPI_1_NSS1_GPIO_PORT, SPI_1_NSS1_GPIO_PIN); \ } #define SPI_1_SCK_H() GPIO_SetBits(SPI_1_SCK_GPIO_PORT, SPI_1_SCK_GPIO_PIN) #define SPI_1_MISO_H() GPIO_SetBits(SPI_1_MISO_GPIO_PORT, SPI_1_MISO_GPIO_PIN) #define SPI_1_MOSI_H() GPIO_SetBits(SPI_1_MOSI_GPIO_PORT, SPI_1_MOSI_GPIO_PIN) #define SPI_1_NSS_H(n) if(n == 1) \ { \ GPIO_SetBits(SPI_1_NSS1_GPIO_PORT, SPI_1_NSS1_GPIO_PIN); \ } #define SPI_1_MISO_READ() GPIO_ReadInputDataBit(SPI_1_MISO_GPIO_PORT,SPI_1_MISO_GPIO_PIN) #endif
bsp_spi_1.c
#include "bsp_spi_1.h" #if SPI_1_MODE == 0 /** * @brief SPI_1引脚配置 * @param 无 * @retval 无 */ static void SPI_1_GPIO_Config(void) { SPI_1_GPIO_CLOCKCMD(SPI_1_GPIO_CLK,ENABLE); //使能SPI引脚相关的时钟 GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; // 复用模式 //配置SPI的 SCK引脚 GPIO_InitStruct.GPIO_Pin = SPI_1_SCK_GPIO_PIN; GPIO_Init(SPI_1_SCK_GPIO_PORT, &GPIO_InitStruct); //配置SPI的 MISO引脚 GPIO_InitStruct.GPIO_Pin = SPI_1_MISO_GPIO_PIN; GPIO_Init(SPI_1_MISO_GPIO_PORT, &GPIO_InitStruct); //配置SPI的 MOSI引脚 GPIO_InitStruct.GPIO_Pin = SPI_1_MOSI_GPIO_PIN; GPIO_Init(SPI_1_MOSI_GPIO_PORT, &GPIO_InitStruct); //配置SPI的 NSS引脚 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; // 输出模式 GPIO_InitStruct.GPIO_Pin = SPI_1_NSS1_GPIO_PIN; GPIO_Init(SPI_1_NSS1_GPIO_PORT, &GPIO_InitStruct); //停止信号 CS引脚高电平 SPI_1_NSS_H(1); } /** * @brief SPI_1工作参数配置 * @param 无 * @retval 无 */ static void SPI_1_MODE_Config(void) { //使能SPI外设时钟 SPI_1_CLOCKCMD(SPI_1_CLK, ENABLE ); //设置引脚复用 GPIO_PinAFConfig(SPI_1_SCK_GPIO_PORT, SPI_1_SCK_GPIO_PinSource, SPI_1_SCK_GPIO_AF); GPIO_PinAFConfig(SPI_1_MISO_GPIO_PORT, SPI_1_MISO_GPIO_PinSource, SPI_1_MISO_GPIO_AF); GPIO_PinAFConfig(SPI_1_MOSI_GPIO_PORT, SPI_1_MOSI_GPIO_PinSource, SPI_1_MOSI_GPIO_AF); SPI_InitTypeDef SPI_InitStruct; // FLASH芯片 支持SPI模式0及模式3,据此设置CPOL CPHA SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStruct.SPI_Mode = SPI_Mode_Master; SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; SPI_InitStruct.SPI_CPOL = SPI_CPOL_High; SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge; SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStruct.SPI_CRCPolynomial = 7; SPI_Init(SPI_1, &SPI_InitStruct); //使能 SPI SPI_Cmd(SPI_1 , ENABLE); } /** * @brief SPI_1初始化 * @param 无 * @retval 无 */ void SPI_1_ConfigInit(void) { SPI_1_GPIO_Config(); SPI_1_MODE_Config(); } #elif SPI_1_MODE == 1 //软件SPI /** * @brief SPI_1引脚配置 * @param 无 * @retval 无 */ static void SPI_1_GPIO_Config(void) { SPI_1_GPIO_CLOCKCMD(SPI_1_GPIO_CLK,ENABLE); //使能SPI引脚相关的时钟 GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //配置SPI的 SCK引脚 GPIO_InitStruct.GPIO_Pin = SPI_1_SCK_GPIO_PIN; GPIO_Init(SPI_1_SCK_GPIO_PORT, &GPIO_InitStruct); //配置SPI的 MOSI引脚 GPIO_InitStruct.GPIO_Pin = SPI_1_MOSI_GPIO_PIN; GPIO_Init(SPI_1_MOSI_GPIO_PORT, &GPIO_InitStruct); //配置SPI的 NSS引脚 GPIO_InitStruct.GPIO_Pin = SPI_1_NSS1_GPIO_PIN; GPIO_Init(SPI_1_NSS1_GPIO_PORT, &GPIO_InitStruct); //配置SPI的 MISO引脚 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; // 输出模式 GPIO_InitStruct.GPIO_Pin = SPI_1_MISO_GPIO_PIN; GPIO_Init(SPI_1_MISO_GPIO_PORT, &GPIO_InitStruct); SPI_1_SCK_L(); //sck初始电平为低电平 SPI_1_NSS_H(1); } /** * @brief SPI_1配置 * @param 无 * @retval 无 */ void SPI_1_ConfigInit(void) { SPI_1_GPIO_Config(); } /** * @brief SPI_1软件延时 * @param 无 * @retval 无 */ static void SPI_1_Delay(uint32_t Time) { for (uint32_t i=0;i<30*Time;i++); //上面的时间是通过逻辑分析仪测试得到的 //工作条件:CPU主频72MHz ,MDK编译环境,1级优化 //循环次数为10时,SCL频率 = 205KHz //循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us //循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us } #if CPOL == 0 && CPHA == 0 //模式0 空闲时处于低电平,奇数时钟边沿采样 /** * @brief SPI发送数据 * @param Data:要发送的数据 * @retval 无 */ static void SPI_1_SendData(uint8_t Data) { for (uint8_t i=0;i<8;i++) { SPI_1_SCK_L(); if (Data & 0x80) //高电平 { SPI_1_MOSI_H(); } else //低电平 { SPI_1_MOSI_L(); } Data <<= 1; SPI_1_Delay(1); SPI_1_SCK_H(); SPI_1_Delay(1); } SPI_1_SCK_L(); } /** * @brief SPI接收数据 * @param 无 * @retval Data:接收的数据 */ static uint8_t SPI_1_ReceiveData(void) { uint8_t data = 0x00; for (uint8_t i=0;i<8;i++) { SPI_1_SCK_L(); data <<= 1; if (SPI_1_MISO_READ())//读数据 { data |= 0x01; } else { data &= 0xfe; } SPI_1_Delay(1); SPI_1_SCK_H(); SPI_1_Delay(1); } SPI_1_SCK_L(); return data; } #elif CPOL == 1 && CPHA == 0 //模式2 空闲时处于高电平,奇数边沿采样 /** * @brief SPI发送数据 * @param Data:要发送的数据 * @retval 无 */ static void SPI_1_SendData(uint8_t Data) { for (uint8_t i=0;i<8;i++) { SPI_1_SCK_H(); if (Data & 0x80) //高电平 { SPI_1_MOSI_H(); } else //低电平 { SPI_1_MOSI_L(); } Data <<= 1; SPI_1_Delay(1); SPI_1_SCK_L(); SPI_1_Delay(1); } SPI_1_SCK_H(); } /** * @brief SPI接收数据 * @param 无 * @retval Data:接收的数据 */ static uint8_t SPI_1_ReceiveData(void) { uint8_t data = 0x00; for (uint8_t i=0;i<8;i++) { SPI_1_SCK_H(); data <<= 1; if (SPI_1_MISO_READ())//读数据 { data |= 0x01; } else { data &= 0xfe; } SPI_1_Delay(1); SPI_1_SCK_L(); SPI_1_Delay(1); } SPI_1_SCK_H(); return data; } #elif CPOL == 0 && CPHA == 1 //模式1 空闲时处于低电平,偶数边沿采样 /** * @brief SPI发送数据 * @param Data:要发送的数据 * @retval 无 */ static void SPI_1_SendData(uint8_t Data) { SPI_1_SCK_L(); for (uint8_t i=0;i<8;i++) { SPI_1_SCK_H(); if (Data & 0x80) //高电平 { SPI_1_MOSI_H(); } else //低电平 { SPI_1_MOSI_L(); } Data <<= 1; SPI_1_Delay(1); SPI_1_SCK_L(); SPI_1_Delay(1); } } /** * @brief SPI接收数据 * @param 无 * @retval Data:接收的数据 */ static uint8_t SPI_1_ReceiveData(void) { uint8_t data = 0x00; SPI_1_SCK_L(); for (uint8_t i=0;i<8;i++) { SPI_1_SCK_H(); data <<= 1; if (SPI_1_MISO_READ())//读数据 { data |= 0x01; } else { data &= 0xfe; } SPI_1_Delay(1); SPI_1_SCK_L(); //偶数边沿采样 SPI_1_Delay(1); } return data; } #elif CPOL == 1 && CPHA == 1 //模式3 空闲时处于高电平,偶数边沿采样 /** * @brief SPI发送数据 * @param Data:要发送的数据 * @retval 无 */ static void SPI_1_SendData(uint8_t Data) { SPI_1_SCK_H(); for (uint8_t i=0;i<8;i++) { SPI_1_SCK_L(); if (Data & 0x80) //高电平 { SPI_1_MOSI_H(); } else //低电平 { SPI_1_MOSI_L(); } Data <<= 1; SPI_1_Delay(1); SPI_1_SCK_H(); SPI_1_Delay(1); } } /** * @brief SPI接收数据 * @param 无 * @retval Data:接收的数据 */ static uint8_t SPI_1_ReceiveData(void) { uint8_t data = 0x00; SPI_1_SCK_H(); for (uint8_t i=0;i<8;i++) { SPI_1_SCK_L(); data <<= 1; if (SPI_1_MISO_READ())//读数据 { data |= 0x01; } else { data &= 0xfe; } SPI_1_Delay(1); SPI_1_SCK_H(); SPI_1_Delay(1); } return data; } #endif /** * @brief SPI_1读数据 * @param Slave_Addr:从机地址 * Read_Addr:要读取的内存地址 * RAM_Size:内存地址大小 1字节 2字节 4字节 * @retval 返回读取的数据 */ uint32_t SPI_1_ReadByte(uint8_t Slave_Addr, uint8_t Read_Addr, uint8_t RAM_Size) { uint32_t data = 0; SPI_1_NSS_H(Slave_Addr); //NSS初始化为高电平 SPI_1_NSS_L(Slave_Addr); //NSS置为低电平,开启SPI传输 SPI_1_SendData(Read_Addr); for (uint8_t i=0;i<RAM_Size;i++) { data = data << 8; data = data | SPI_1_ReceiveData(); } SPI_1_NSS_H(Slave_Addr); //关闭SPI数据传输 return Read_Data; } /** * @brief SPI_1写数据 * @param Slave_Addr:从机地址 * Write_Addr:要写入的内存地址 * Write_Data:要写入的数据 * RAM_Size:内存地址大小 1字节 2字节 4字节 * @retval 无 */ void SPI_1_WriteByte(uint8_t Slave_Addr, uint8_t Write_Addr, uint32_t Write_Data, uint8_t RAM_Size) { uint8_t data; SPI_1_NSS_H(Slave_Addr); //NSS初始化为高电平 SPI_1_NSS_L(Slave_Addr); //NSS置为低电平,开启SPI传输 SPI_1_SendData(Write_Addr); for (uint8_t i=0;i<RAM_Size;i++) { data = Write_Data >> ((RAM_Size-i-1)*8); SPI_1_SendData(data); } SPI_1_NSS_H(Slave_Addr); //关闭SPI数据传输 } /** * @brief SPI_1读数据 * @param Slave_Addr:从机地址 * Read_Addr:要读取的内存地址 * Read_Num:要读取的数据长度 * p_Read_Buffer:缓存读取的数据 * @retval 返回读取的数据 */ void SPI_1_ReadBuffer(uint8_t Slave_Addr, uint8_t Read_Addr, uint8_t Read_Num, uint8_t* p_Read_Buffer) { SPI_1_NSS_H(Slave_Addr); //NSS初始化为高电平 SPI_1_NSS_L(Slave_Addr); //NSS置为低电平,开启SPI传输 for (uint8_t i=0;i<Read_Num;i++) { SPI_1_SendData(Read_Addr+i); SPI_1_DelayMS(1); p_Read_Buffer[i] = SPI_1_ReceiveData(); } SPI_1_NSS_H(Slave_Addr); //关闭SPI数据传输 } /** * @brief SPI_1写数据 * @param Slave_Addr:从机地址 * Write_Addr:要写入的内存地址 * Write_Num:要写入的数据长度 * p_Write_Buffer:要写入的数据 * @retval 无 */ void SPI_1_WriteBuffer(uint8_t Slave_Addr, uint8_t Write_Addr, uint8_t Write_Num, uint8_t* p_Write_Buffer) { SPI_1_NSS_H(Slave_Addr); //NSS初始化为高电平 SPI_1_NSS_L(Slave_Addr); //NSS置为低电平,开启SPI传输 for (uint8_t i=0;i<Write_Num;i++) { SPI_1_SendData(Write_Addr+i); SPI_1_DelayMS(1); SPI_1_SendData(p_Write_Buffer[i]); } SPI_1_NSS_H(Slave_Addr); //关闭SPI数据传输 } #endif
flash_drive_function.h
#ifndef __FLASH_DRIVE_FUNCTION_H #define __FLASH_DRIVE_FUNCTION_H #include "main.h" #define FLASH_SPI SPI5 //Flash连接的SPI #define FLASH_SPI_NSS 1 //FLASH寄存器地址和指令 #define W25X_WRITE_ENABLE 0x06 //打开写数据指令 #define W25X_WRITE_DISABLE 0x04 //关闭写数据指令 #define W25X_READ_STATUS_REGISTER 0x05 //读工作状态寄存器指令 #define W25X_WRITE_STATUS_REGISTER 0x01 //写状态寄存器指令 #define W25X_READ_DATA 0x03 //读数据指令 #define W25X_FAST_READ_DATA 0x0B #define W25X_FAST_READ_DUAL_OUTPUT 0x3B #define W25X_PAGE_PROGRAM 0x02 //页写入指令 #define W25X_SECTOR_ERASE 0x20 //扇区擦除指令4KB #define W25X_BLOCK_ERASE 0xD8 //块擦除指令64KB #define W25X_CHIP_ERASE 0xC7 //整个芯片全部擦除指令 #define W25X_POWER_DOWN 0xB9 //掉电指令 #define W25X_RELEASE_POWER_DOWN 0xAB //上电指令 #define W25X_DEVICE_ID 0xAB //读取Device_ID指令 #define W25X_MANUFACT 0x90 #define W25X_JEDEC_ID 0x9F //厂商号+芯片ID #define WIP_FLAG 0x01 //WIP(BUSY)标志位,该位置0表示写入数据完毕 #define DUMMY_BYTE 0xFF //读Flash芯片的状态寄存器,该参数可以是任意数据 //Flash的ID号 //#define FLASH_ID 0xEF3015 //W25X16 //#define FLASH_ID 0xEF4015 //W25Q16 //#define FLASH_ID 0XEF4017 //W25Q64 #define FLASH_ID 0xEF4018 //W25Q128 //Flash的页大小 #define FLASH_PAGE_SIZE 256 //Flsah每页的大小 //#define FLASH_PAGE_SIZE 4096 //扇区的读写地址 #define FLASH_WRITE_ADDRESS 0x00000 #define FLASH_READ_ADDRESS FLASH_WRITE_ADDRESS #define FLASH_SECTOR_ERASE_ADDRESS FLASH_WRITE_ADDRESS //Flash通信等待超时时间 #define FLASH_SHORT_WAIT_TIMEOUT ((uint32_t)0x1000) #define FLASH_LONG_WAIT_TIMEOUT ((uint32_t)(100 * FLASH_SHORT_WAIT_TIMEOUT)) #define FLASH_SPI_NSS_L() SPI_1_NSS_L(FLASH_SPI_NSS) #define FLASH_SPI_NSS_H() SPI_1_NSS_H(FLASH_SPI_NSS) /******************************************************************************************* 函数声明 *******************************************************************************************/ uint8_t FLASH_ReadDeviceID(void); uint32_t FLASH_ReadJedecID(void); void FLASH_PowerDownMode(void); void FLASH_PowerOnMode(void); void FLASH_EraseSectors(uint32_t Sector_Addr); void FLASH_EraseChip(void); void FLASH_WritePage(uint32_t Write_Addr, uint16_t Write_Num, uint8_t* p_Write_Buffer); void FLASH_WriteBuffer(uint32_t Write_Addr, uint16_t Write_Num, uint8_t* p_Write_Buffer); void FLASH_ReadBuffer(uint32_t Read_Addr, uint16_t Read_Num ,uint8_t* p_Read_Buffer); uint8_t FLASH_CharTest(void); uint8_t FLASH_IntDoubleTest(void); #endif
flash_drive_function.c
#include "flash_drive_function.h" /****************************************************************************************************************** FLASH通信规则: 与 EEPROM 一样,由于 FLASH 芯片向内部存储矩阵写入数据需要消耗一定的时间,并不是在总线通讯结束的一瞬间完成的, 所以在写操作后需要确认 FLASH 芯片“空闲”时才能进行再次写入。为了表示自己的工作状态, FLASH 芯片定义了一个状态 寄存器(Read_Status_Register 0x05) 由于 FLASH 存储器的特性决定了它只能把原来为“ 1”的数据位改写成“0”,而原来为“ 0”的数据位不能直接改写为“ 1”。所 以这里涉及到数据“擦除”的概念,在写入前,必须要对目标存储矩阵进行擦除操作,把矩阵中的数据位擦除为“ 1”,在数据 写入的时候,如果要存储数据“ 1”,那就不修改存储矩阵 ,在要存储数据“ 0”时,才更改该位。 通常,对存储矩阵擦除的基本操作单位都是多个字节进行,FLASH 芯片支持“扇区擦除(4KB)”、“块擦除(64KB)”以及“整片擦 除(全部擦除)”。擦除时地址需要需要按4KB或64KB对齐. FLASH数据写入的最小单位是“页”不是“字节”,一页有256字节。页写入地址不需要按256字节对齐,只需要确定要写入的地址 处于擦除状态即可。 在实际应用中一般都以扇区(4KB)为单位进行擦写。 **********************************************************************************************************************/ static __IO uint32_t flash_spi_timeout = FLASH_LONG_WAIT_TIMEOUT; static uint8_t flash_error_code=0; //错误代号 /** * @brief 打印FLASH通信出错的地方 * @param Error_Code:错误代码,可以用来定位是哪个环节出错. * @retval 返回1,表示FLASH读取失败. */ static uint8_t FLASH_ErrorCode(uint8_t Error_Code) { printf("SPI 等待超时!eeprom_error_code = %d\n",Error_Code); return 1; } /** * @brief 向Flash发送一个字节的数据 * @param Write_Data:要发送的数据 * @retval -1:失败 */ static uint8_t FLASH_WriteByte(uint8_t Write_Data) { flash_spi_timeout = FLASH_SHORT_WAIT_TIMEOUT; while (SPI_I2S_GetFlagStatus(FLASH_SPI, SPI_I2S_FLAG_TXE) == RESET)//等待发送缓冲区为空,TXE事件 { if ((flash_spi_timeout--) == 0) { DBG_PRINTF("Flash_SPI发送数据超时!"); flash_error_code = 1; return FLASH_ErrorCode(flash_error_code); } } SPI_I2S_SendData(FLASH_SPI, Write_Data); //写入数据寄存器,把要写入的数据写入发送缓冲区 flash_spi_timeout = FLASH_SHORT_WAIT_TIMEOUT; while (SPI_I2S_GetFlagStatus(FLASH_SPI, SPI_I2S_FLAG_RXNE) == RESET) //等待接收缓冲区非空,RXNE事件 { if ((flash_spi_timeout--) == 0) { DBG_PRINTF("Flash_SPI接收数据超时!"); flash_error_code = 2; return FLASH_ErrorCode(flash_error_code); } } //读取数据寄存器,获取接收缓冲区数据 return SPI_I2S_ReceiveData(FLASH_SPI); } /** * @brief 从Flash读取一个字节的数据 * @param 无 * @retval 返回接收到的数据 */ static uint8_t FLASH_ReadByte(void) { return (FLASH_WriteByte(DUMMY_BYTE)); } /** * @brief 等待WIP(BUSY)标志被置0,即等待向FLASH内部写入数据完毕 * @param 无 * @retval 无 */ static void FLASH_WaitForWriteEnd(void) { uint8_t flash_status = 0; FLASH_SPI_NSS_L(); FLASH_WriteByte(W25X_READ_STATUS_REGISTER); //发送 读工作状态寄存器 命令 flash_spi_timeout = FLASH_LONG_WAIT_TIMEOUT; do //若FLASH忙碌,则等待 { flash_status = FLASH_ReadByte(); //读取FLASH芯片的状态寄存器 { if ((flash_spi_timeout--) == 0) { DBG_PRINTF("Flash处于忙碌状态!"); flash_error_code = 3; FLASH_ErrorCode(flash_error_code); return ; } } } while ((flash_status & WIP_FLAG) == SET); //正在写入 FLASH_SPI_NSS_H(); } /** * @brief 读取FLASH Device ID * @param 无 * @retval FLASH Device ID */ uint8_t FLASH_ReadDeviceID(void) { uint8_t temp = 0; FLASH_SPI_NSS_L(); //开始通讯:CS低电平 FLASH_WriteByte(W25X_DEVICE_ID); FLASH_ReadByte(); //任意数据 FLASH_ReadByte(); //任意数据 FLASH_ReadByte(); //任意数据 temp = FLASH_ReadByte(); //芯片ID FLASH_SPI_NSS_H(); //停止通讯:CS高电平 return temp; } /** * @brief 读取FLASH的ID * @param 无 * @retval FLASH ID */ uint32_t FLASH_ReadJedecID(void) { uint8_t temp1 = 0, temp2 = 0, temp3 = 0; uint32_t temp4 = 0; FLASH_SPI_NSS_L(); //开始通讯:CS低电平 FLASH_WriteByte(W25X_JEDEC_ID); //发送JEDEC指令,读取ID temp1 = FLASH_ReadByte(); //厂商号 temp2 = FLASH_ReadByte(); //芯片ID temp3 = FLASH_ReadByte(); //芯片ID FLASH_SPI_NSS_H(); //停止通讯:CS高电平 temp4 = temp4 | temp1; temp4 = (temp4 << 8) | temp2; temp4 = (temp4 << 8) | temp3; return temp4; } /** * @brief 睡眠,进入掉电模式 * @param 无 * @retval 无 */ void FLASH_PowerDownMode(void) { FLASH_SPI_NSS_L(); //开始通讯:CS低电平 FLASH_WriteByte(W25X_POWER_DOWN); //发送 掉电 命令 FLASH_SPI_NSS_H(); //停止通讯:CS高电平 } /** * @brief 唤醒,进入上电模式 * @param 无 * @retval 无 */ void FLASH_PowerOnMode(void) { FLASH_SPI_NSS_L(); //开始通讯:CS低电平 FLASH_WriteByte(W25X_RELEASE_POWER_DOWN); //发送 上电 命令 FLASH_SPI_NSS_H(); //停止通讯:CS高电平 } /** * @brief 擦除指定的FLASH扇区(4KB) * @param SectorAddr:要擦除的扇区地址,地址要对齐到4KB * @retval 无 */ void FLASH_EraseSectors(uint32_t Sector_Addr) { FLASH_SPI_NSS_L(); FLASH_WriteByte(W25X_WRITE_ENABLE); //发送写使能命令 FLASH_SPI_NSS_H(); FLASH_WaitForWriteEnd(); //读取写入数据是否完毕 FLASH_SPI_NSS_L(); FLASH_WriteByte(W25X_SECTOR_ERASE); //发送擦除指定扇区指令 FLASH_WriteByte((Sector_Addr & 0xFF0000) >> 16); //发送擦除扇区的地址---高位 FLASH_WriteByte((Sector_Addr & 0xFF00) >> 8); //发送擦除扇区的地址---中位 FLASH_WriteByte(Sector_Addr & 0xFF); //发送擦除扇区的地址---低位 FLASH_SPI_NSS_H(); FLASH_WaitForWriteEnd(); //读取写入数据是否完毕,即等待擦除完毕 } /** * @brief 擦除整个芯片,整个芯片擦除时间会比较长 * @param 无 * @retval 无 */ void FLASH_EraseChip(void) { FLASH_SPI_NSS_L(); FLASH_WriteByte(W25X_WRITE_ENABLE); //发送写使能命令 FLASH_SPI_NSS_H(); FLASH_WaitForWriteEnd(); //读取写入数据是否完毕 FLASH_SPI_NSS_L(); FLASH_WriteByte(W25X_CHIP_ERASE); //发送整块擦除指令 FLASH_SPI_NSS_H(); FLASH_WaitForWriteEnd(); //读取写入数据是否完毕,即等待擦除完毕 } /** * @brief 对FLASH写入多个数据,写入的数据必须小于等于Flash一页的大小。调用本函数写入数据前需要先擦除扇区 * @param Write_Addr:要写入的地址 * Write_Num:要写入的数据长度,必须小于等于SPI_FLASH_PerWritePageSize * p_Write_Buffer:指向要写入的数据 * @retval 无 */ void FLASH_WritePage(uint32_t Write_Addr, uint16_t Write_Num, uint8_t* p_Write_Buffer) { FLASH_SPI_NSS_L(); //通讯开始:CS低 FLASH_WriteByte(W25X_WRITE_ENABLE); //发送写使能命令 FLASH_SPI_NSS_H(); //通讯结束:CS高 FLASH_SPI_NSS_L(); FLASH_WriteByte(W25X_PAGE_PROGRAM); //发送页写指令 FLASH_WriteByte((Write_Addr & 0xFF0000) >> 16); //发送写地址的高位 FLASH_WriteByte((Write_Addr & 0xFF00) >> 8); //发送写地址的中位 FLASH_WriteByte(Write_Addr & 0xFF); //发送写地址的低位 if (Write_Num > FLASH_PAGE_SIZE) { Write_Num = FLASH_PAGE_SIZE; printf("FLASH_PageSize too large!"); } while (Write_Num--) //写入数据 { FLASH_WriteByte(*p_Write_Buffer); //发送当前要写入的字节数据 p_Write_Buffer++; //指向下一字节数据 } FLASH_SPI_NSS_H(); //停止信号 FLASH: CS 高电平 FLASH_WaitForWriteEnd(); //等待写入完毕 } /** * @brief 该函数可以将缓冲区中的多个数据连续写到FLASH中,调用本函数写入数据前需要先擦除扇区 * @param Write_Addr:要写入的地址 * Write_Num:要写入的数据个数 * p_Write_Buffer:该指针指向要写入的数据 * @retval 无 */ void FLASH_WriteBuffer(uint32_t Write_Addr, uint16_t Write_Num, uint8_t* p_Write_Buffer) { uint8_t WR_Addr = 0, Less_Num = 0, Whole_Page_Num = 0, Residue_Num = 0; WR_Addr = Write_Addr % FLASH_PAGE_SIZE; //mod运算求余,若Write_Addr是FLASH_Page_Size整数倍,运算结果Addr值为0 Less_Num = FLASH_PAGE_SIZE - WR_Addr; //差count个数据值,刚好可以对齐到页地址 Whole_Page_Num = Write_Num / FLASH_PAGE_SIZE; //计算出要写多少整数页 Residue_Num = Write_Num % FLASH_PAGE_SIZE; //计算出剩余不满一页的字节数 if (WR_Addr == 0) //WR_Addr=0,则Write_Addr刚好按页对齐 { if (Whole_Page_Num == 0) //要写的数据不满一页 { FLASH_WritePage(Write_Addr, Write_Num, p_Write_Buffer); } else //要写的数据满一页 { while (Whole_Page_Num--) //先把整数页都写了 { FLASH_WritePage(Write_Addr, FLASH_PAGE_SIZE, p_Write_Buffer); //写一页 Write_Addr = Write_Addr + FLASH_PAGE_SIZE; p_Write_Buffer = p_Write_Buffer + FLASH_PAGE_SIZE; } FLASH_WritePage(Write_Addr, Residue_Num, p_Write_Buffer); //若有多余的不满一页的数据,把它写完 } } else //若地址与 SPI_FLASH_PageSize 不对齐 { if (Whole_Page_Num == 0) //要写的数据不满一页 { if (Residue_Num > Less_Num) //当前页剩余的Less_Num个位置比Residue_Num小,当前页写不完 { uint32_t temp = Residue_Num - Less_Num; //当前页写不完的字节数 FLASH_WritePage(Write_Addr, Less_Num , p_Write_Buffer); //先写满当前页 Write_Addr = Write_Addr + Less_Num; p_Write_Buffer = p_Write_Buffer + Less_Num; FLASH_WritePage(Write_Addr, temp , p_Write_Buffer); //再写剩余的数据 } else //当前页剩余的Less_Num个位置能写完Write_Num个数据 { FLASH_WritePage(Write_Addr, Write_Num, p_Write_Buffer); } } else //要写的数据满一页 { Write_Num = Write_Num - Less_Num; //地址不对齐多出的count分开处理,不加入这个运算 Whole_Page_Num = Write_Num / FLASH_PAGE_SIZE; Residue_Num = Write_Num % FLASH_PAGE_SIZE; FLASH_WritePage(Write_Addr, Less_Num , p_Write_Buffer); Write_Addr = Write_Addr + Less_Num; p_Write_Buffer = p_Write_Buffer + Less_Num; while (Whole_Page_Num--) //把整数页都写了 { FLASH_WritePage(Write_Addr, FLASH_PAGE_SIZE, p_Write_Buffer); Write_Addr = Write_Addr + FLASH_PAGE_SIZE; p_Write_Buffer = p_Write_Buffer + FLASH_PAGE_SIZE; } if (Residue_Num != 0) //若有多余的不满一页的数据,把它写完 { FLASH_WritePage(Write_Addr, Residue_Num, p_Write_Buffer); } } } } /** * @brief 读取FLASH数据 * @param Read_Addr:读取地址 * Read_Num:读取数据长度 * p_Read_Buffer:存储读出数据的指针 * @retval 无 */ void FLASH_ReadBuffer(uint32_t Read_Addr, uint16_t Read_Num ,uint8_t* p_Read_Buffer) { FLASH_SPI_NSS_L(); FLASH_WriteByte(W25X_READ_DATA); //发送读指令 FLASH_WriteByte((Read_Addr & 0xFF0000) >> 16); //发送要读区的地址的---高位 FLASH_WriteByte((Read_Addr& 0xFF00) >> 8); //发送要读区的地址的---中位 FLASH_WriteByte(Read_Addr & 0xFF); //发送要读区的地址的---低位 while (Read_Num--) //读取数据 { *p_Read_Buffer = FLASH_ReadByte(); //读取一个字节 p_Read_Buffer++; } FLASH_SPI_NSS_H(); //停止信号 FLASH: CS 高电平 } /************************************************************************** 测试代码 **************************************************************************/ /** * @brief 简单延时函数 * @param 无 * @retval 无 */ static void FLASH_Delay(__IO uint32_t Time) //简单的延时函数 { for (; Time != 0; Time--); } /** * @brief 单字节读写测试 * @param 无 * @retval 0::正常 1:异常 */ uint8_t FLASH_CharTest(void) { uint8_t device_id; uint32_t jedec_id; uint8_t write_buf[20]; uint8_t read_buf[20]; //填充测试缓冲区 for (uint32_t i=0;i<sizeof(write_buf);i++) { write_buf[i] = i; } device_id = FLASH_ReadDeviceID(); jedec_id = FLASH_ReadJedecID(); printf("\r\nDevice_ID is 0x%X, Jedec_ID is 0x%X\r\n", device_id, jedec_id); if(jedec_id == FLASH_ID) //检验 Flash ID { printf("\r\n检测到SPI FLASH W25Q128 !\r\n"); FLASH_EraseSectors(FLASH_SECTOR_ERASE_ADDRESS); //擦除将要写入的 FLASH 扇区,FLASH写入前要先擦除 // FLASH_EraseChip();//整片擦除,时间比较长 // FLASH_WritePage(FLASH_WRITE_ADDRESS, sizeof(write_buf),write_buf); FLASH_WriteBuffer(FLASH_WRITE_ADDRESS, sizeof(write_buf),write_buf); //将发送缓冲区的数据写到flash中 printf("\r\n写入的数据为:\r\n"); for (uint32_t i=0;i<sizeof(write_buf);i++) { printf("0x%x ",write_buf[i]); } FLASH_Delay(0XFFFF); FLASH_ReadBuffer(FLASH_READ_ADDRESS, sizeof(read_buf),read_buf); //将刚刚写入的数据读出来放到接收缓冲区中 printf("\r\n读出的数据为:\r\n"); for (uint32_t i=0;i<sizeof(read_buf);i++) { printf("0x%x ",read_buf[i]); } for (uint32_t i=0;i<sizeof(write_buf);i++) { if(write_buf[i] != read_buf[i]) { printf("\r\n16M串行flash(W25Q128)测试失败!\r\n"); break; } } } return 0; } /** * @brief 整数和小数读写测试 * @param 无 * @retval 0:正常 1:异常 */ uint8_t FLASH_IntDoubleTest(void) { uint8_t device_id; uint32_t jedec_id; int write_buf_int[10]; int read_buf_int[10]; long double write_buf_double[10]; long double read_buf_double[10]; uint16_t int_addr = 0; uint16_t double_addr = 256; for (uint32_t i=0;i<10;i++) { write_buf_int[i]=500*i+3*i; write_buf_double[i] = i+i*0.2; } device_id = FLASH_ReadDeviceID(); jedec_id = FLASH_ReadJedecID(); printf("\r\nDevice_ID is 0x%X, Jedec_ID is 0x%X\r\n", device_id, jedec_id); if (jedec_id == FLASH_ID) //检验 Flash ID { printf("\r\n检测到SPI FLASH W25Q128 !\r\n"); FLASH_EraseSectors(FLASH_SECTOR_ERASE_ADDRESS); //擦除将要写入的 FLASH 扇区,FLASH写入前要先擦除 // FLASH_Erase_Chip();//整片擦除,时间比较长 FLASH_WriteBuffer(int_addr, sizeof(write_buf_int),(void *)write_buf_int); //往EEPROM中写入10个整形数据 FLASH_WriteBuffer(double_addr, sizeof(write_buf_double),(void *)write_buf_double); //往EEPROM中写入10个小数数据 FLASH_Delay(0x0FFFF); //写完之后需要适当的延时再去读,不然会出错 FLASH_ReadBuffer(int_addr, sizeof(read_buf_int),(void *)read_buf_int); //从EEPROM中读10个整形数据 FLASH_ReadBuffer(double_addr, sizeof(read_buf_double),(void *)read_buf_double); //从EEPROM中读10个小数数据 for (uint32_t i=0;i<10;i++) { if (read_buf_int[i] != write_buf_int[i]) { printf("0x%d ", read_buf_int[i]); printf("错误:FLASH读出与写入的数据不一致"); return 1; } printf(" %d", read_buf_int[i]); } for (uint32_t i=0;i<10;i++) { if (read_buf_double[i] != write_buf_double[i]) { printf("0x%f ", read_buf_double[i]); printf("错误:FLASH读出与写入的数据不一致"); return 1; } printf(" %LF", read_buf_double[i]); } printf("FLASH读写测试成功\r\n"); } }
                    
                
                
            
        
浙公网安备 33010602011771号