注:本文侧重于直接应用,涉及到的一些底层原理还请自行搜索了解
涉及到的硬软件信息如下:
单片机:STM32F407VET6
FLASH芯片:W25Q16(其他型号代码中只需要做适当修改即可),关于不同型号的的FLASH芯片,可以参考如下链接,讲解的很详细
Stm32mx版本:6.4
话不多说,直接上正文
2.sys配置,基本配置不用多说
3.spi配置
4.配置串口以方便调试,可以打开DMA,默认即可,就不贴图了
5.配置FatFs,MAX_SS (Maximum Sector Size)4096,意思是一个扇区的字节数(1扇区有16页,1页256字节,16*256=4096)
6.使用的IO情况
7.时钟树参考
8.生成代码
1.添加w25q.c和w25q.h文件
w25q.c
#include "w25q.h"
/**********************************************************************************
* 函数功能: 模块初始化
*/
uint8_t BSP_W25Qx_Init(void)
{
BSP_W25Qx_Reset();
return BSP_W25Qx_GetStatus();
}
static void BSP_W25Qx_Reset(void)
{
uint8_t cmd[2] = {RESET_ENABLE_CMD,RESET_MEMORY_CMD};
HAL_GPIO_WritePin(NSS_GPIO_Port,NSS_Pin,GPIO_PIN_RESET);
/* Send the reset command */
HAL_SPI_Transmit(&hspi1, cmd, 2, W25Qx_TIMEOUT_VALUE);
HAL_GPIO_WritePin(NSS_GPIO_Port,NSS_Pin,GPIO_PIN_SET);
}
/**********************************************************************************
* 函数功能: 获取设备状态
*/
static uint8_t BSP_W25Qx_GetStatus(void)
{
uint8_t cmd[] = {READ_STATUS_REG1_CMD};
uint8_t status;
HAL_GPIO_WritePin(NSS_GPIO_Port,NSS_Pin,GPIO_PIN_RESET);
/* Send the read status command */
HAL_SPI_Transmit(&hspi1, cmd, 1, W25Qx_TIMEOUT_VALUE);
/* Reception of the data */
HAL_SPI_Receive(&hspi1,&status, 1, W25Qx_TIMEOUT_VALUE);
HAL_GPIO_WritePin(NSS_GPIO_Port,NSS_Pin,GPIO_PIN_SET);
/* Check the value of the register */
if((status & W25Q128FV_FSR_BUSY) != 0)
{
return W25Qx_BUSY;
}
else
{
return W25Qx_OK;
}
}
/**********************************************************************************
* 函数功能: 写使能
*/
uint8_t BSP_W25Qx_WriteEnable(void)
{
uint8_t cmd[] = {WRITE_ENABLE_CMD};
uint32_t tickstart = HAL_GetTick();
/*Select the FLASH: Chip Select low */
HAL_GPIO_WritePin(NSS_GPIO_Port,NSS_Pin,GPIO_PIN_RESET);
/* Send the read ID command */
HAL_SPI_Transmit(&hspi1, cmd, 1, W25Qx_TIMEOUT_VALUE);
/*Deselect the FLASH: Chip Select high */
HAL_GPIO_WritePin(NSS_GPIO_Port,NSS_Pin,GPIO_PIN_SET);
/* Wait the end of Flash writing */
while(BSP_W25Qx_GetStatus() == W25Qx_BUSY)
{
/* Check for the Timeout */
if((HAL_GetTick() - tickstart) > W25Qx_TIMEOUT_VALUE)
{
return W25Qx_TIMEOUT;
}
}
return W25Qx_OK;
}
/**********************************************************************************
* 函数功能: 获取设备ID
*/
void BSP_W25Qx_Read_ID(uint16_t *ID)
{
uint8_t cmd[4] = {READ_ID_CMD,0x00,0x00,0x00};
uint8_t temp[2];
HAL_GPIO_WritePin(NSS_GPIO_Port,NSS_Pin,GPIO_PIN_RESET);
/* Send the read ID command */
HAL_SPI_Transmit(&hspi1, cmd, 4, W25Qx_TIMEOUT_VALUE);
/* Reception of the data */
HAL_SPI_Receive(&hspi1,temp, 2, W25Qx_TIMEOUT_VALUE);
*ID = (temp[0]<<8)|temp[1];
HAL_GPIO_WritePin(NSS_GPIO_Port,NSS_Pin,GPIO_PIN_SET);
}
/**********************************************************************************
* 函数功能: 读数据
* 输入参数: 缓存数组指针、读地址、字节数
*/
uint8_t BSP_W25Qx_Read(uint8_t* pData, uint32_t ReadAddr, uint32_t Size)
{
uint8_t cmd[4];
/* Configure the command */
cmd[0] = READ_CMD;
cmd[1] = (uint8_t)(ReadAddr >> 16);
cmd[2] = (uint8_t)(ReadAddr >> 8);
cmd[3] = (uint8_t)(ReadAddr);
HAL_GPIO_WritePin(NSS_GPIO_Port,NSS_Pin,GPIO_PIN_RESET);
/* Send the read ID command */
HAL_SPI_Transmit(&hspi1, cmd, 4, W25Qx_TIMEOUT_VALUE);
/* Reception of the data */
if (HAL_SPI_Receive(&hspi1, pData,Size,W25Qx_TIMEOUT_VALUE) != HAL_OK)
{
return W25Qx_ERROR;
}
HAL_GPIO_WritePin(NSS_GPIO_Port,NSS_Pin,GPIO_PIN_SET);
return W25Qx_OK;
}
/**********************************************************************************
* 函数功能: 写数据
* 输入参数: 缓存数组指针、写地址、字节数
*/
uint8_t BSP_W25Qx_Write(uint8_t* pData, uint32_t WriteAddr, uint32_t Size)
{
uint8_t cmd[4];
uint32_t end_addr, current_size, current_addr;
uint32_t tickstart = HAL_GetTick();
/* Calculation of the size between the write address and the end of the page */
current_addr = 0;
while (current_addr <= WriteAddr)
{
current_addr += W25Q128FV_PAGE_SIZE;
}
current_size = current_addr - WriteAddr;
/* Check if the size of the data is less than the remaining place in the page */
if (current_size > Size)
{
current_size = Size;
}
/* Initialize the adress variables */
current_addr = WriteAddr;
end_addr = WriteAddr + Size;
/* Perform the write page by page */
do
{
/* Configure the command */
cmd[0] = PAGE_PROG_CMD;
cmd[1] = (uint8_t)(current_addr >> 16);
cmd[2] = (uint8_t)(current_addr >> 8);
cmd[3] = (uint8_t)(current_addr);
/* Enable write operations */
BSP_W25Qx_WriteEnable();
HAL_GPIO_WritePin(NSS_GPIO_Port,NSS_Pin,GPIO_PIN_RESET);
/* Send the command */
if (HAL_SPI_Transmit(&hspi1,cmd, 4, W25Qx_TIMEOUT_VALUE) != HAL_OK)
{
return W25Qx_ERROR;
}
/* Transmission of the data */
if (HAL_SPI_Transmit(&hspi1, pData,current_size, W25Qx_TIMEOUT_VALUE) != HAL_OK)
{
return W25Qx_ERROR;
}
HAL_GPIO_WritePin(NSS_GPIO_Port,NSS_Pin,GPIO_PIN_SET);
/* Wait the end of Flash writing */
while(BSP_W25Qx_GetStatus() == W25Qx_BUSY)
{
/* Check for the Timeout */
if((HAL_GetTick() - tickstart) > W25Qx_TIMEOUT_VALUE)
{
return W25Qx_TIMEOUT;
}
}
/* Update the address and size variables for next page programming */
current_addr += current_size;
pData += current_size;
current_size = ((current_addr + W25Q128FV_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : W25Q128FV_PAGE_SIZE;
} while (current_addr < end_addr);
return W25Qx_OK;
}
/**********************************************************************************
* 函数功能: 扇区擦除
* 输入参数: 地址
*/
uint8_t BSP_W25Qx_Erase_Sector(uint32_t Address)
{
uint8_t cmd[4];
uint32_t tickstart = HAL_GetTick();
cmd[0] = SECTOR_ERASE_CMD;
cmd[1] = (uint8_t)(Address >> 16);
cmd[2] = (uint8_t)(Address >> 8);
cmd[3] = (uint8_t)(Address);
/* Enable write operations */
BSP_W25Qx_WriteEnable();
/*Select the FLASH: Chip Select low */
HAL_GPIO_WritePin(NSS_GPIO_Port,NSS_Pin,GPIO_PIN_RESET);
/* Send the read ID command */
HAL_SPI_Transmit(&hspi1, cmd, 4, W25Qx_TIMEOUT_VALUE);
/*Deselect the FLASH: Chip Select high */
HAL_GPIO_WritePin(NSS_GPIO_Port,NSS_Pin,GPIO_PIN_SET);
/* Wait the end of Flash writing */
while(BSP_W25Qx_GetStatus() == W25Qx_BUSY)
{
/* Check for the Timeout */
if((HAL_GetTick() - tickstart) > W25Q128FV_SECTOR_ERASE_MAX_TIME)
{
return W25Qx_TIMEOUT;
}
}
return W25Qx_OK;
}
/**********************************************************************************
* 函数功能: 芯片擦除
*/
uint8_t BSP_W25Qx_Erase_Chip(void)
{
uint8_t cmd[4];
uint32_t tickstart = HAL_GetTick();
cmd[0] = CHIP_ERASE_CMD;
/* Enable write operations */
BSP_W25Qx_WriteEnable();
/*Select the FLASH: Chip Select low */
HAL_GPIO_WritePin(NSS_GPIO_Port,NSS_Pin,GPIO_PIN_RESET);
/* Send the read ID command */
HAL_SPI_Transmit(&hspi1, cmd, 1, W25Qx_TIMEOUT_VALUE);
/*Deselect the FLASH: Chip Select high */
HAL_GPIO_WritePin(NSS_GPIO_Port,NSS_Pin,GPIO_PIN_SET);
/* Wait the end of Flash writing */
while(BSP_W25Qx_GetStatus() != W25Qx_BUSY)
{
/* Check for the Timeout */
if((HAL_GetTick() - tickstart) > W25Q128FV_BULK_ERASE_MAX_TIME)
{
return W25Qx_TIMEOUT;
}
}
return W25Qx_OK;
}
w25q.h
#ifndef __w25q_H
#define __w25q_H
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx.h"
#include "spi.h"
//一块有16个扇区,一个扇区有16页,一页256个字节
#define W25Q128FV_FLASH_SIZE 0x1000000 /* 128 MBits => 16MBytes *///总容量
#define W25Q128FV_SECTOR_SIZE 0x10000 /* 256 sectors of 64KBytes *///页数量256(块)*16(扇区)*16(页)
#define W25Q128FV_SUBSECTOR_SIZE 0x1000 /* 4096 subsectors of 4kBytes *///一个扇区的字节数
#define W25Q128FV_PAGE_SIZE 0x100 /* 65536 pages of 256 bytes *///一页的字节数
#define W25Q16FV_FLASH_SIZE 0x200000 /* 16 MBits => 2MBytes *///容量
#define W25Q16FV_SECTOR_SIZE 0x2000 /* 32 sectors of 64KBytes *///扇区数32*16*16
#define W25Q16FV_SUBSECTOR_SIZE 0x1000 /* 4096 subsectors of 4kBytes *///4096
#define W25Q16FV_PAGE_SIZE 0x100 /* 65536 pages of 256 bytes */ //256
#define W25Q128FV_DUMMY_CYCLES_READ 4
#define W25Q128FV_DUMMY_CYCLES_READ_QUAD 10
#define W25Q128FV_BULK_ERASE_MAX_TIME 250000
#define W25Q128FV_SECTOR_ERASE_MAX_TIME 3000
#define W25Q128FV_SUBSECTOR_ERASE_MAX_TIME 800
#define W25Qx_TIMEOUT_VALUE 1000
/* Reset Operations */
#define RESET_ENABLE_CMD 0x66
#define RESET_MEMORY_CMD 0x99
#define ENTER_QPI_MODE_CMD 0x38
#define EXIT_QPI_MODE_CMD 0xFF
/* Identification Operations */
#define READ_ID_CMD 0x90
#define DUAL_READ_ID_CMD 0x92
#define QUAD_READ_ID_CMD 0x94
#define READ_JEDEC_ID_CMD 0x9F
/* Read Operations */
#define READ_CMD 0x03
#define FAST_READ_CMD 0x0B
#define DUAL_OUT_FAST_READ_CMD 0x3B
#define DUAL_INOUT_FAST_READ_CMD 0xBB
#define QUAD_OUT_FAST_READ_CMD 0x6B
#define QUAD_INOUT_FAST_READ_CMD 0xEB
/* Write Operations */
#define WRITE_ENABLE_CMD 0x06
#define WRITE_DISABLE_CMD 0x04
/* Register Operations */
#define READ_STATUS_REG1_CMD 0x05
#define READ_STATUS_REG2_CMD 0x35
#define READ_STATUS_REG3_CMD 0x15
#define WRITE_STATUS_REG1_CMD 0x01
#define WRITE_STATUS_REG2_CMD 0x31
#define WRITE_STATUS_REG3_CMD 0x11
/* Program Operations */
#define PAGE_PROG_CMD 0x02
#define QUAD_INPUT_PAGE_PROG_CMD 0x32
/* Erase Operations */
#define SECTOR_ERASE_CMD 0x20
#define CHIP_ERASE_CMD 0xC7
#define PROG_ERASE_RESUME_CMD 0x7A
#define PROG_ERASE_SUSPEND_CMD 0x75
/* Flag Status Register */
#define W25Q128FV_FSR_BUSY ((uint8_t)0x01) /*!< busy */
#define W25Q128FV_FSR_WREN ((uint8_t)0x02) /*!< write enable */
#define W25Q128FV_FSR_QE ((uint8_t)0x02) /*!< quad enable */
#define W25Qx_Enable() HAL_GPIO_WritePin(W25QXX_CS_GPIO_Port, W25QXX_CS_Pin, GPIO_PIN_RESET)
#define W25Qx_Disable() HAL_GPIO_WritePin(W25QXX_CS_GPIO_Port, W25QXX_CS_Pin, GPIO_PIN_SET)
#define W25Qx_OK ((uint8_t)0x00)
#define W25Qx_ERROR ((uint8_t)0x01)
#define W25Qx_BUSY ((uint8_t)0x02)
#define W25Qx_TIMEOUT ((uint8_t)0x03)
uint8_t BSP_W25Qx_Init(void);
static void BSP_W25Qx_Reset(void);
static uint8_t BSP_W25Qx_GetStatus(void);
uint8_t BSP_W25Qx_WriteEnable(void);
//void BSP_W25Qx_Read_ID(uint8_t *ID);
void BSP_W25Qx_Read_ID(uint32_t *ID);
uint8_t BSP_W25Qx_Read(uint8_t* pData, uint32_t ReadAddr, uint32_t Size);
uint8_t BSP_W25Qx_Write(uint8_t* pData, uint32_t WriteAddr, uint32_t Size);
uint8_t BSP_W25Qx_Erase_Block(uint32_t Address);
uint8_t BSP_W25Qx_Erase_Chip(void);
#endif
2.打开图下标注的文件,添加w25q.h头文件
3. 标注处添加如下代码 if(W25Qx_OK ==BSP_W25Qx_Init())
{
Stat = RES_OK;
}
4.标注处添加如下代码 BSP_W25Qx_Read_ID(&FlashID);
if(FlashID == 0xEF14)//不同的flash芯片此处的0xEF14需要做修改,参照文章开始的链接
{
Stat = RES_OK;
}
5.标注处添加如下代码 BSP_W25Qx_Read(buff, sector*4096, count*4096);
6. 标注处添加如下代码 BSP_W25Qx_Erase_Sector(sector*4096);
BSP_W25Qx_Write((BYTE *)buff, sector*4096, count*4096);7.标注处添加如下代码
1.回到main.c,添加几个变量
3.接好串口线,连接电脑打开串口调试助手,查看输出信息,小工告成!!
1.使用printf要添加一下代码并在main.h里面包含"stdio.h"
int fputc(int ch, FILE *f) //需要调用stdio.h,并且勾选target下的use microlib
{
uint8_t temp[1]={ch};
HAL_UART_Transmit(&huart1,temp,1,2);
return ch;
}
2.使用strlen记得包含“string.h”头文件
博主本人小趴菜一枚,本文用于记录个人学习过程笔记,文中若是有错误的地方还请大家谅解和指正,收到后方好及时改正!!如有疑问也请私信或评论,收到第一时间一定及时回复!!最后附上以上的原工程文件,若有需要可以直接点击下载!!