【随手笔记】FLASH-W25Q16(三)

#include "bsp_w25q16.h"

/*内部函数声明区*/
static HAL_StatusTypeDef bsp_w25q_Transmit(uint8_t * T_pData, uint16_t T_Size);
static HAL_StatusTypeDef bsp_w25q_Receive(uint8_t * R_pData, uint16_t R_Size);


/*内部函数定义区*/


/*
函数参数:
        1、T_pData:发送数据缓冲区中取出数据发送出去
        2、T_Size :需要发送的数据的长度
*/
static HAL_StatusTypeDef bsp_w25q_Transmit(uint8_t * T_pData, uint16_t T_Size)
{
    return HAL_SPI_Transmit(&W25Q_SPI, T_pData, T_Size, 0xffff);
}

/*
函数参数:
        1、R_pData:接收数据并放置到接收数据缓冲区中
        2、R_Size :需要接收的数据的长度
*/
static HAL_StatusTypeDef bsp_w25q_Receive(uint8_t * R_pData, uint16_t R_Size)
{
    return HAL_SPI_Receive(&W25Q_SPI, R_pData, R_Size, 0xffff);
}

/*
写使能或失能
    参数:
        Type:
            1、为1时使能
            2、为0时失能
*/
HAL_StatusTypeDef Bsp_Write_En_De(uint8_t Type)
{
    uint8_t cmd;
    HAL_StatusTypeDef STD = HAL_ERROR;

    W25Q_CS_Level(0);
    SPI_Delay(1);
    switch(Type)
    {
        case 1:
            cmd = W25Q_W_EN;
            break;
        case 0:
            cmd = W25Q_W_DIS;
            break;
        default:
            cmd = W25Q_W_DIS;
            break;
    }

    if(bsp_w25q_Transmit(&cmd, 1) == HAL_OK)
    {
        STD = HAL_OK;
    }

    W25Q_CS_Level(1);
    return STD;
}

/*
读状态寄存器
    参数:Select
        1、为0时是寄存器1
        2、为1时是寄存器2
    参数:State(指针)
        1、返回的状态标志

    流程:先写入命令,然后读取状态
*/
HAL_StatusTypeDef Bsp_Read_State_Reg(uint8_t Select, uint8_t* State)
{
    uint8_t cmd;
    HAL_StatusTypeDef STD = HAL_ERROR;

    W25Q_CS_Level(0);
    SPI_Delay(1);
    switch(Select)
    {
        case 0:
            cmd = W25Q_R_STA_REG1;
            break;
        case 1:
            cmd = W25Q_R_STA_REG2;
            break;
        default:
            cmd = W25Q_R_STA_REG1;
            break;
    }

    if(bsp_w25q_Transmit(&cmd, 1) == HAL_OK)
    {
        if(bsp_w25q_Receive(State,1) == HAL_OK)
        {
            STD = HAL_OK;
        }
    }

    W25Q_CS_Level(1);

    return STD;
}

/*
判忙
        用处:判断当前flash是否在忙碌状态
        BUSY只读寄存器
        忙时高电平
        空闲低电平
*/
void Bsp_Judge_Busy(void)
{
    uint8_t State;
    do{
        Bsp_Read_State_Reg(0, &State);  //不要用指针类型局部变量传进去,必被卡死
        State &= 0x01;
    }while(State == 0x01);//寄存器为1时为忙
}


/*
读数据
            参数:R_Addr
                        1、读取数据的地址
            参数:R_Data(数组指针)
                        1、获取读取的数据
            参数:R_Size
                        1、读取的数据的大小
*/
HAL_StatusTypeDef Bsp_W25Q_Read_Data(uint32_t R_Addr, uint8_t * R_Data, uint16_t R_Size)
{
    HAL_StatusTypeDef STD = HAL_ERROR;

    uint8_t addr[4] = {0};
    addr[0] = W25Q_R_Data;
    addr[1] = (R_Addr >> 16) & 0xFF;
    addr[2] = (R_Addr >> 8) & 0xFF;
    addr[3] = (R_Addr) & 0xFF;

    Bsp_Judge_Busy();   //判忙
    W25Q_CS_Level(0);
    SPI_Delay(1);

    if(bsp_w25q_Transmit(addr, 4) == HAL_OK)
    {
        if(bsp_w25q_Receive(R_Data,R_Size) == HAL_OK)
        {
            STD = HAL_OK;
        }
    }

    W25Q_CS_Level(1);

    return STD;
}


/*
写数据-页
    页的描述:1字节到 256 字节(一页)
    页写的前提条件:编程之前必须保证额你存空间是0xff,
    所以得先进行擦除(擦除后模式全为1)
    页写的注意事项:进行页编程时,如果数据字节数超过了 256 字节,
    地址将自动回到页的起始地址,覆盖掉之前的数据。

    参数:WriteAddr
                1、地址,三个字节地址
    参数:PW_Data(数组指针)
                1、要写入的数据,长度根据PW_size来定
                2、高位先传
    参数:PW_Size
                2、要写入的数据长度

    先通过状态寄存器判断是否在忙,
    然后写使能,
    接着判忙,
    拉底片选选中,
    发命令字节0x02,
    发三个地址,高位优先,
    然后发你要写的数据,
    最后拉高片选
*/

HAL_StatusTypeDef Bsp_W25Q_Page_Write(uint32_t WriteAddr, uint8_t * PW_Data, uint16_t PW_Size)
{
    HAL_StatusTypeDef STD = HAL_ERROR;

    uint8_t addr[4] = {0};
    addr[0] = W25Q_Page_Program;
    addr[1] = (WriteAddr >> 16) & 0xFF;
    addr[2] = (WriteAddr >> 8) & 0xFF;
    addr[3] = (WriteAddr) & 0xFF;

    Bsp_Judge_Busy();   //判忙
    Bsp_Write_En_De(1);

    Bsp_Judge_Busy();
    W25Q_CS_Level(0);
    SPI_Delay(1);

    if(bsp_w25q_Transmit(addr, 4) == HAL_OK)
    {
        if(bsp_w25q_Transmit(PW_Data, PW_Size) == HAL_OK)
        {
            STD = HAL_OK;
        }
    }

    W25Q_CS_Level(1);
    Bsp_Judge_Busy();

    return STD;
}



/*
扇区擦除
    扇区的描述:W25Q16总共2MB,分为32块(每块64KB),
                            每块16个扇区,每个扇区4K个字节。
    扇区的备注:W25Q16的最小擦除单位就是一个扇区
                            所以至少给芯片开辟一个4KB的缓存区,
                            以防止一次性删除太多,而丢失数据。(显然单片机不会给他开辟这么大的空间)

    参数:num : 扇区的序号


*/
HAL_StatusTypeDef Bsp_W25Q_Sector_Erase(uint32_t num)
{
    HAL_StatusTypeDef STD = HAL_ERROR;

    //一个扇区有4KB的大小,
    //为了使找到对应的扇区地址,所以要乘以4KB
    //(1<<12) = 4096
    num *= (1<<12);

    uint8_t addr[4] = {0};
    addr[0] = W25Q_Sector_Erase;
    addr[1] = (num >> 16) & 0xFF;
    addr[2] = (num >> 8) & 0xFF;
    addr[3] = (num) & 0xFF;

    Bsp_Judge_Busy();
    Bsp_Write_En_De(1);

    Bsp_Judge_Busy();
    W25Q_CS_Level(0);
    SPI_Delay(1);

    if(bsp_w25q_Transmit(addr, 4) == HAL_OK)
    {
        STD = HAL_OK;
    }

    W25Q_CS_Level(1);
    Bsp_Judge_Busy();

    return STD;
}



/*
块擦除
    块的描述:W25Q16有2MB的容量,而2MB有32个块,所以1块有64kB的大小,
                        所以这个函数一次能擦除64KB的大小。

    参数:Block_Addr
                1、块地址,共32个块,对应32个地址,以64K为单位寻址
                2、高位先传

    流程:先开写使能、判忙,再写命令,
                再写3个字节的地址,最后写失能

      参数:num : 块的序号
*/
HAL_StatusTypeDef   Bsp_W25Q_Block_Erase(uint32_t num)
{
    uint8_t cmd = W25Q_Block_Erase;
    HAL_StatusTypeDef STD = HAL_ERROR;

    //总共有32个块,而一个块有64KB的大小,
    //为了使找到对应的块地址,所以要乘以64KB
    num *= (1<<16);

    uint8_t addr[4] = {0};
    addr[0] = W25Q_Block_Erase;
    addr[1] = (num >> 16) & 0xFF;
    addr[2] = (num) & 0xFF;
    addr[3] = (num) & 0xFF;

    Bsp_Judge_Busy();
    Bsp_Write_En_De(1);

    Bsp_Judge_Busy();
    W25Q_CS_Level(0);
    SPI_Delay(1);

    if(bsp_w25q_Transmit(addr, 4) == HAL_OK)
    {
        STD = HAL_OK;
    }

    W25Q_CS_Level(1);
    Bsp_Judge_Busy();
    return STD;
}


/*
全片擦除
    描述:直接把芯片全部擦除
*/
HAL_StatusTypeDef Bsp_W25Q_Full_Erase(void)
{
    uint8_t cmd = W25Q_Full_Erase;
    HAL_StatusTypeDef STD = HAL_ERROR;

    Bsp_Judge_Busy();
    Bsp_Write_En_De(1);

    Bsp_Judge_Busy();
    W25Q_CS_Level(0);
    SPI_Delay(1);

    if(bsp_w25q_Transmit(&cmd, 1) == HAL_OK)
    {
        STD = HAL_OK;
    }

    W25Q_CS_Level(1);
    Bsp_Judge_Busy();

    return STD;
}


/*
读ID
    描述:读3个字节分别是生产厂家、存储器类型、容量
*/
HAL_StatusTypeDef Bsp_Read_Jedec_ID(uint8_t * R_Jedec_ID)
{
    uint8_t read_id[3] = {0};
    uint8_t cmd = W25Q_JEDEC_ID;
    HAL_StatusTypeDef STD = HAL_ERROR;

    Bsp_Judge_Busy();
    W25Q_CS_Level(0);
    SPI_Delay(1);

    if(bsp_w25q_Transmit(&cmd, 1) == HAL_OK)
    {
        if(bsp_w25q_Receive(read_id, 3) == HAL_OK)
        {
            STD = HAL_OK;
        }
    }

    W25Q_CS_Level(1);

    if(STD == HAL_OK)
    {
        LOG_OUT("read_id= %x %x %x\r\n", read_id[0], read_id[1], read_id[2]);
    }
    else
    {
        LOG_OUT("read_id= err\r\n");
    }
    return STD;
}

/*
 * 电源开启
 */
void W26Q16_Power_On(void)
{
    MX_SPI1_Init();
    //W25Q_WP_HIGH;
    rt_thread_mdelay(100);
    Bsp_Read_Jedec_ID(NULL);

}

/*
 * 电源关闭
 */
void W26Q16_Power_Off(void)
{
    //WP拉低开启读保护
    //W25Q_WP_LOW;

    W25Q_CS_Level(GPIO_PIN_RESET);
    rt_thread_mdelay(10);

    //失能SPI配置为IO
    w25q16_init_sleep();
    rt_thread_mdelay(1000);
}

#ifndef __DRV_W25Q16_H
#define __DRV_W25Q16_H

#include "app_user.h"
#include "spi.h"

/*句柄重命名*/
#define W25Q_SPI                hspi1

#define SPI_Delay(ms)           rt_thread_mdelay(ms+1)

/*片选引脚定义与函数调用*/
#define W25Q_CS_Pin             GPIO_PIN_4
#define W25Q_CS_Port            GPIOA

#define W25Q_CS_Level(_CS_STATE__)      HAL_GPIO_WritePin(W25Q_CS_Port, W25Q_CS_Pin, (GPIO_PinState)_CS_STATE__)

//#define W25Q_WP_HIGH   HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, GPIO_PIN_SET)
//#define W25Q_WP_LOW    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, GPIO_PIN_RESET)

#define W25Q_W_EN               0x06        //写使能
#define W25Q_W_DIS              0x04        //写禁止

#define W25Q_R_Data             0x03        //读数据

#define W25Q_R_STA_REG1     0x05        //读状态寄存器1,紧跟着的字节就是当前状态
#define W25Q_R_STA_REG2     0x35        //读状态寄存器2,紧跟着的字节就是当前状态

#define W25Q_W_STA_REG      0x01        //写状态寄存器,写入两个字节,分别到寄存器1,和寄存器2

#define W25Q_Page_Program   0x02        //页编程,先跟3个地址字节,再跟一个数据字节

#define W25Q_Block_Erase    0xD8        //块擦除64k,三个地址字节

#define W25Q_Sector_Erase   0x20        //扇区擦除,跟三个地址

#define W25Q_Full_Erase     0xC7        //全片擦除//0x60

#define W25Q_Susp_Erase     0x75        //暂停擦除

#define W25Q_Rest_Erase     0x7A        //恢复擦除

#define W25Q_PowDow_Mode    0xB9        //掉电模式

#define W25Q_HPer_Mode      0xA3        //高性能模式

#define W25Q_JEDEC_ID       0x9F        //读3个字节分别是生产厂家、存储器类型、容量

/*写使能或失能*/
HAL_StatusTypeDef Bsp_Write_En_De(uint8_t Type);

/*读状态寄存器*/
HAL_StatusTypeDef Bsp_Read_State_Reg(uint8_t Select, uint8_t* State);

/*判忙*/
void Bsp_Judge_Busy(void);

/*读数据*/
HAL_StatusTypeDef Bsp_W25Q_Read_Data(uint32_t R_Addr, uint8_t * R_Data, uint16_t R_Size);

/*页写*/
HAL_StatusTypeDef Bsp_W25Q_Page_Write(uint32_t WriteAddr, uint8_t * PW_Data, uint16_t PW_Size);

/*扇区擦除*/
HAL_StatusTypeDef Bsp_W25Q_Sector_Erase(uint32_t num);

/*块擦除*/
HAL_StatusTypeDef   Bsp_W25Q_Block_Erase(uint32_t num);

/*全片擦除*/
HAL_StatusTypeDef Bsp_W25Q_Full_Erase(void);

/*读ID*/
HAL_StatusTypeDef Bsp_Read_Jedec_ID(uint8_t * R_Jedec_ID);

void W26Q16_Power_On(void);
void W26Q16_Power_Off(void);
#endif /*W25Q16__H__*/

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    spi.c
  * @brief   This file provides code for the configuration
  *          of the SPI instances.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "spi.h"

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

SPI_HandleTypeDef hspi1;

/* SPI1 init function */
void MX_SPI1_Init(void)
{

  /* USER CODE BEGIN SPI1_Init 0 */

  /* USER CODE END SPI1_Init 0 */

  /* USER CODE BEGIN SPI1_Init 1 */

  /* USER CODE END SPI1_Init 1 */
  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;
  hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 7;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SPI1_Init 2 */

  /* USER CODE END SPI1_Init 2 */

}

void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(spiHandle->Instance==SPI1)
  {
  /* USER CODE BEGIN SPI1_MspInit 0 */

  /* USER CODE END SPI1_MspInit 0 */
    /* SPI1 clock enable */
    __HAL_RCC_SPI1_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**SPI1 GPIO Configuration
    PA5     ------> SPI1_SCK
    PA6     ------> SPI1_MISO
    PA7     ------> SPI1_MOSI
    */
    GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF0_SPI1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* USER CODE BEGIN SPI1_MspInit 1 */

  /* USER CODE END SPI1_MspInit 1 */
  }
}

void HAL_SPI_MspDeInit(SPI_HandleTypeDef* spiHandle)
{

  if(spiHandle->Instance==SPI1)
  {
  /* USER CODE BEGIN SPI1_MspDeInit 0 */

  /* USER CODE END SPI1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_SPI1_CLK_DISABLE();

    /**SPI1 GPIO Configuration
    PA5     ------> SPI1_SCK
    PA6     ------> SPI1_MISO
    PA7     ------> SPI1_MOSI
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7);

  /* USER CODE BEGIN SPI1_MspDeInit 1 */

  /* USER CODE END SPI1_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */


void w25q16_init_sleep(void)
{
    extern SPI_HandleTypeDef hspi1;
    HAL_SPI_DeInit(&hspi1);

    __HAL_RCC_GPIOA_CLK_ENABLE();

     GPIO_InitTypeDef GPIO_InitStruct = {0};

     GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
     GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
     GPIO_InitStruct.Pull = GPIO_PULLDOWN;
     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
     HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

     HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_RESET);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值