stm32通过SPI挂载FatFs读写串行FLASH(stm32cubemx)

注:本文侧重于直接应用,涉及到的一些底层原理还请自行搜索了解

涉及到的硬软件信息如下:

单片机:STM32F407VET6

FLASH芯片:W25Q16(其他型号代码中只需要做适当修改即可),关于不同型号的的FLASH芯片,可以参考如下链接,讲解的很详细

Stm32mx版本:6.4

话不多说,直接上正文

一、配置Cubemx

1.时钟配置,若使用内部时钟则跳过此步

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.标注处添加如下代码

switch(cmd){
        case GET_SECTOR_COUNT:
            *(DWORD *)buff = 512;
        break;
        case GET_SECTOR_SIZE:
            *(DWORD *)buff = 4096;
        break;
        case GET_BLOCK_SIZE:
            *(DWORD *)buff = 1;
        break;       
    }
 三、测试是否成功

1.回到main.c,添加几个变量

 2.编写测试代码

 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”头文件 

 五、最后

博主本人小趴菜一枚,本文用于记录个人学习过程笔记,文中若是有错误的地方还请大家谅解和指正,收到后方好及时改正!!如有疑问也请私信或评论,收到第一时间一定及时回复!!最后附上以上的原工程文件,若有需要可以直接点击下载!!

  • 5
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
单片机STM32F4可以通过SPI接口与SD卡进行通信,并使用FATFS库来实现对SD卡的读写操作。下面是一个简单示例程序。 首先,需要在STM32F4上配置SPI接口。在STM32CubeMX中配置SPI相关引脚,并设置SPI的时钟频率、数据位长度等参数。 然后,在代码中包含相关的库文件和头文件。 ```c #include "stm32f4xx_hal.h" #include "fatfs.h" #include "ff.h" ``` 使用以下代码初始化FATFS库: ```c FATFS fs; FIL file; FRESULT res; res = f_mount(&fs, "0:", 1); if(res != FR_OK) { // 初始化错误处理 } ``` 接下来,可以通过以下代码打开SD卡上的文件并进行读写操作: ```c res = f_open(&file, "test.txt", FA_READ | FA_WRITE | FA_CREATE_ALWAYS); if(res != FR_OK) { // 打开文件错误处理 } // 写入数据到文件 const char* data = "Hello, SD card!"; UINT bytes_written; res = f_write(&file, data, strlen(data), &bytes_written); if(res != FR_OK) { // 写入数据错误处理 } // 读取文件数据 char read_buffer[100]; UINT bytes_read; res = f_read(&file, read_buffer, sizeof(read_buffer), &bytes_read); if(res != FR_OK) { // 读取数据错误处理 } // 关闭文件 res = f_close(&file); if(res != FR_OK) { // 关闭文件错误处理 } ``` 最后,使用以下代码卸载SD卡和释放FATFS资源: ```c res = f_mount(NULL, "0:", 1); if(res != FR_OK) { // 卸载SD卡错误处理 } ``` 这是一个简单的单片机STM32F4通过SPI与SD卡进行读写操作的示例程序。具体的代码细节和错误处理可以根据实际需求进行修改和完善。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值