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"); 
    }
}

 

posted @ 2020-08-13 22:00  孤情剑客  阅读(256)  评论(0)    收藏  举报