STM32单片机,通过Flash模拟U盘运用FATFS管理文件
这是最近项目上多次需要这个功能,于是倒腾了有些时候,其中参考了很多网上的资料,其次还咨询了我的一个王师兄,帮助了我很多,在此表示感谢。好了下面开始正儿八经的东西了。1.他们的关系是什么?完成这个任务需要先理清楚Flash、FATFS、虚拟U盘、STM32单片(单片机)机这几者之间的关系,Flash是一个存贮介质单片机会对其进行数据管理。而FATFS是一个操作系统,相当于自动的帮助我们管理数据,从数据结构的角度上来说就是管理入栈出栈以及数据的表头表尾中间位置数据的删除添加等等一系列的操作,这个都已经帮助我们完成了。这三个已经可以实现了一个完整的功能,就是单片机通过某种协议比如下面会使用的SPI通讯协议对Flash进行读写数据,而为了便于操作我们在单片机上面移植了FATFS文件管理系统,对于数据的管理更加的便携。现在你已经实现了单片机上的数据管理,但是现在你想把数据在电脑上显示,于是有一个叫虚拟U盘这个东西出现,它通过数据线将电脑与单片机连接起来,希望单片机可以像U盘一样把数据显示在电脑端,所以这个便叫虚拟U盘(自己瞎比喻的)。而实现这个功能就是通过CubeMx配置单片机上面的USB端口而实现这一功能。ok,下面开始实操。2.对Flash进行操作本项目使用的存储芯片是W25Q128,所以下述内容都基于此内容完成。还有更多的存储介质以及芯片需要不同的驱动代码。下面是本项目的驱动代码。W25Qxx.c(代码出自:从 0 搭建 SPI Flash 文件系统:驱动、FatFS、读写与坑点 | 时光的轨迹)#include "W25Qxx.h" static void W25Qxx_Reset(void); W25Qxx_Info_t W25Qxx_Info = {0}; __weak void W25Qxx_CS_Enable(void) { } __weak void W25Qxx_CS_Disable(void) { } // 返回1是正常 __weak uint8_t W25Qxx_ReadByte(uint8_t* RxData, uint16_t Size) { (void)RxData; (void)Size; return 0; } // 返回1是正常 __weak uint8_t W25Qxx_WriteByte(uint8_t* TxData, uint16_t Size) { (void)TxData; (void)Size; return 0; } __weak uint32_t W25Qxx_GetTick(void) { return 0; } /** * @brief Initializes the W25Q128FV interface. * @retval None */ uint8_t W25Qxx_Init(void) { /* Reset W25Qxx */ W25Qxx_Reset(); return W25Qxx_GetStatus(); } /** * @brief This function reset the W25Qx. * @retval None */ static void W25Qxx_Reset(void) { uint8_t cmd[2] = {RESET_ENABLE_CMD, RESET_MEMORY_CMD}; W25Qxx_CS_Enable(); /* Send the reset command */ W25Qxx_WriteByte(cmd, 2); W25Qxx_CS_Disable(); } /** * @brief Reads current status of the W25Q128FV. * @retval W25Q128FV memory status */ uint8_t W25Qxx_GetStatus(void) { uint8_t cmd[] = {READ_STATUS_REG1_CMD}; uint8_t status; W25Qxx_CS_Enable(); /* Send the read status command */ W25Qxx_WriteByte(cmd, 1); /* Reception of the data */ W25Qxx_ReadByte(status, 1); W25Qxx_CS_Disable(); /* Check the value of the register */ if((status W25QXX_FSR_BUSY) != 0) { return W25QXX_BUSY; } else { return W25QXX_OK; } } /** * @brief This function send a Write Enable and wait it is effective. * @retval None */ uint8_t W25Qxx_WriteEnable(void) { uint8_t cmd[] = {WRITE_ENABLE_CMD}; uint32_t tickstart = W25Qxx_GetTick(); /*Select the FLASH: Chip Select low */ W25Qxx_CS_Enable(); /* Send the read ID command */ W25Qxx_WriteByte(cmd, 1); /*Deselect the FLASH: Chip Select high */ W25Qxx_CS_Disable(); /* Wait the end of Flash writing */ while(W25Qxx_GetStatus() == W25QXX_BUSY) { /* Check for the Timeout */ if((W25Qxx_GetTick() - tickstart) W25QXX_TIMEOUT_VALUE) { return W25QXX_TIMEOUT; } } return W25QXX_OK; } /** * @brief Read Manufacture/Device ID. * @param return value address * @retval None */ void W25Qxx_Read_ID(uint8_t* ID) { uint8_t cmd[4] = {READ_JEDEC_ID_CMD, 0x00, 0x00, 0x00}; W25Qxx_CS_Enable(); /* Send the read ID command */ W25Qxx_WriteByte(cmd, 1); /* Reception of the data */ W25Qxx_ReadByte(ID, 3); W25Qxx_CS_Disable(); } void W25Qxx_IC_Check(void) { uint32_t count; uint8_t temp_id[3]; /* Read FLASH ID */ W25Qxx_Read_ID(temp_id); W25Qxx_Info.Flash_ID = ((uint32_t)temp_id[0] 16) | ((uint32_t)temp_id[1] 8) | ((uint32_t)temp_id[2]); W25Qxx_Info.Flash_Sector_Count = 0x00; W25Qxx_Info.Flash_Sector_Size = 0x00; switch(W25Qxx_Info.Flash_ID) { /* W25XXX */ case W25X10_FLASH_ID: /* 0xEF3011-----1M bit */ count = 1; break; case W25X20_FLASH_ID: /* 0xEF3012-----2M bit */ count = 2; break; case W25X40_FLASH_ID: /* 0xEF3013-----4M bit */ count = 4; break; case W25X80_FLASH_ID: /* 0xEF4014-----8M bit */ count = 8; break; case W25Q16_FLASH_ID1: /* 0xEF3015-----16M bit */ case W25Q16_FLASH_ID2: /* 0xEF4015-----16M bit */ count = 16; break; case W25Q32_FLASH_ID1: /* 0xEF4016-----32M bit */ case W25Q32_FLASH_ID2: /* 0xEF6016-----32M bit */ count = 32; break; case W25Q64_FLASH_ID1: /* 0xEF4017-----64M bit */ case W25Q64_FLASH_ID2: /* 0xEF6017-----64M bit */ count = 64; break; case W25Q128_FLASH_ID1: /* 0xEF4018-----128M bit */ case W25Q128_FLASH_ID2: /* 0xEF6018-----128M bit */ count = 128; break; case W25Q256_FLASH_ID1: /* 0xEF4019-----256M bit */ case W25Q256_FLASH_ID2: /* 0xEF6019-----256M bit */ count = 256; break; default: if((W25Qxx_Info.Flash_ID != 0xFFFFFFFF) (W25Qxx_Info.Flash_ID != 0x00000000)) { count = 16; } else { count = 0x00; } break; } count = ((uint32_t)count * 1024) * ((uint32_t)1024 / 8); if(count) { // 如果是内部,那么DEF_UDISK_SECTOR_SIZE是512,如果是外部,则DEF_SECTOR_SIZE是4096 W25Qxx_Info.Flash_Sector_Count = count / DEF_SECTOR_SIZE; // DEF_SECTOR_SIZE; W25Qxx_Info.Flash_Sector_Size = DEF_SECTOR_SIZE; // DEF_SECTOR_SIZE; W25Qxx_Info.Flash_Page_Size = 256; // 全系列固定的 } else { // printf ("External Flash not connected\r\n"); // while(1); } } /** * @brief Reads an amount of data from the QSPI memory. * @param pData: Pointer to data to be read * @param ReadAddr: Read start address * @param Size: Size of data to read * @retval QSPI memory status */ // TODO uint8_t W25Qxx_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); W25Qxx_CS_Enable(); /* Send the read ID command */ W25Qxx_WriteByte(cmd, 4); /* Reception of the data */ if(W