呼和浩特网站seo优化方案,创新网站内容建设,学院网站建设开题报告,手工活300元一天基于Mini-F5375-OB开发板的硬件特性#xff08;搭载MM32F5系列高性能MCU、外置ZD25WQ32 SPI Flash及全速USB接口#xff09;#xff0c;本文提出一种双模存储架构设计方案#xff1a;通过在Flash介质上构建统一的FAT文件系统#xff0c;同步实现USB MSC#xff08;Mass S…基于Mini-F5375-OB开发板的硬件特性搭载MM32F5系列高性能MCU、外置ZD25WQ32 SPI Flash及全速USB接口本文提出一种双模存储架构设计方案通过在Flash介质上构建统一的FAT文件系统同步实现USB MSCMass Storage Class设备功能和UART命令行文件管理系统。该设计充分利用MCU硬件资源采用TinyUSB协议栈实现USB大容量存储设备功能将4MB QSPI Flash虚拟为PC可识别的U盘同时集成FatFs文件系统模块通过UART接口提供完整的文件操作命令集包括文件创建、读写、目录管理等。两种访问方式共享同一物理存储空间通过互斥锁机制确保数据一致性其中USB模式采用SCSI命令直接操作Flash底层扇区而UART模式通过FatFs API进行文件级管理。QSPI接口的外置flashZD25WQ32)USB接口一、第三方库使用1、FATFS是一个面向嵌入式系统的轻量级FAT/exFAT文件系统模块由ChaN开发并开源。它采用ANSI C编写具有高度可移植性支持FAT12、FAT16和FAT32文件系统可无缝运行在SD卡、Flash等存储介质上。2、TinyUSB 是一个开源的轻量级 USB 协议栈专为嵌入式系统设计支持主机(host)和设备(device)模式。它采用纯 C 语言编写具有高度可移植性支持多种 MCU 平台。特别适合实现 USB 虚拟串口、U 盘、键盘等设备功能是嵌入式 USB 开发的理想选择。二、工作模式选择由于FATFS和TinyUSB共存并不容易所以本设计通过在启动时检测GPIO引脚电平状态实现两种完全独立的工作模式切换满足不同场景下的需求1.USB存储TinyUSB模式(PB0低电平)将内部Flash虚拟为U盘允许PC端直接访问文件系统适用于数据导出和配置更新2.MCU文件系统操作FATFSUART模式(PB0高电平)启用UART命令行接口运行数据采集任务数据记录到FATFS文件系统适用于现场数据采集场景也就是说在在按下KEY1(PB0)键时上电进入USB存储模式不按KEY1(PB0)键时上电进入MCU文件系统操作模式外置flash做为数据交互媒介。三、基于GPIO电平判断上电进入不同的运行模式复制intmain(void){PLATFORM_Init();//板级驱动不用驱动uart3USART_Configure(115200);//UART3驱动EXTI_Configure();//key1PB0key2(PB1)初始化if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) 0){//判断PB0电平modeChangeRequest1;//如果低电平设置模式1}else{modeChangeRequest0;//如果低电平设置模式0}if(modeChangeRequest1) {//模式1USB存储TinyUSB模式printf(Enter Usb Msc Mode\r\n);QSPI_Configure();//QSPI初始化驱动外部flashTinyUSB_Device_CDC_MSC_Sample();//进入tiny初始化和循环}else//模式0MCU文件系统操作FATFSUART模式{printf(Enter MCU FatFs Mode\r\n);UART_Sample();//进入UART接收命令在FatFs执行模式}while(1)//不会被执行{}}四、USB存储TinyUSB模式实现很遗憾在TinyUSB官方提供的device例程中没有提供基于外部flash的例程这部分需要自己根据TinyUSB接口函数实现1、将以下文件加入工程其中cdc_device.c不是必须的它实现了cdc串口设备。上面圈出的4个文件需要加入工程这四个文件可以在MM32F5270的例程中找到LibSamples_MM32F5270_V1.5.6\3rdPartySoftwarePorting\TinyUSB\Demos\TinyUSB_Device_CDC_MSC其中msc_disk.c原来是基于SRAM的需要在后面修改为基于外部flash。2、复制一个msc_disk.c到工程根目录需要这个文件的结构实现其中的几个接口函数void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4])bool tud_msc_test_unit_ready_cb(uint8_t lun)void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size)bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject)int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)bool tud_msc_is_writable_cb (uint8_t lun)int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize)int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize)3、tud_msc_inquiry_cb实现tud_msc_inquiry_cb 是 TinyUSB 协议栈中用于响应 USB Mass Storage Class (MSC) INQUIRY 命令 的关键回调函数其作用是为主机如 PC提供存储设备的基本标识信息。复制voidtud_msc_inquiry_cb(uint8_tlun,uint8_tvendor_id[8],uint8_tproduct_id[16],uint8_tproduct_rev[4]){(void) lun;constcharvid[] TinyUSB;constcharpid[] Mass Storage;constcharrev[] 1.0;memcpy(vendor_id , vid,strlen(vid));memcpy(product_id , pid,strlen(pid));memcpy(product_rev, rev,strlen(rev));}4、tud_msc_test_unit_ready_cb 实现tud_msc_test_unit_ready_cb 是 TinyUSB 协议栈中用于响应 SCSI Test Unit Ready (TUR) 命令 的关键回调函数其作用是向主机如 PC报告存储设备当前是否就绪并可访问。复制booltud_msc_test_unit_ready_cb(uint8_tlun){(void) lun;// RAM disk is ready until ejectedif(ejected) {// Additional Sense 3A-00 is NOT_FOUNDtud_msc_set_sense(lun, SCSI_SENSE_NOT_READY,0x3a,0x00);returnfalse;}returntrue;}5、tud_msc_capacity_cb函数实现tud_msc_capacity_cb 是 TinyUSB 协议栈中用于响应 USB Mass Storage Class (MSC) 容量查询请求 的核心回调函数其作用是向主机如 PC/Mac报告存储设备的物理容量参数。复制voidtud_msc_capacity_cb(uint8_tlun,uint32_t* block_count,uint16_t* block_size){(void) lun;*block_count DISK_BLOCK_NUM;*block_size DISK_BLOCK_SIZE;}6、tud_msc_start_stop_cb 函数实现tud_msc_start_stop_cb 是 TinyUSB 协议栈中处理 USB Mass Storage Class (MSC) 启停事件 的关键回调函数主要用于响应主机的设备加载/弹出指令和电源状态管理复制booltud_msc_start_stop_cb(uint8_tlun,uint8_tpower_condition,boolstart,boolload_eject){(void) lun;(void) power_condition;if( load_eject ){if(start){// load disk storageejected false;}else{// unload disk storageejected true;}}returntrue;}7、【重点关注】int32_t tud_msc_read10_cb函数实现tud_msc_read10_cb 是 TinyUSB 协议栈中处理 USB Mass Storage Class (MSC) 读取请求 的核心回调函数负责将存储设备的数据通过 USB 传输给主机复制int32_ttud_msc_read10_cb(uint8_tlun,uint32_tlba,uint32_toffset,void* buffer,uint32_tbufsize){(void) lun;//uint32_t addr lba * DISK_BLOCK_SIZE offset;// out of ramdiskif( lba DISK_BLOCK_NUM )return-1;uint32_taddr lba * FLASH_SECTOR_SIZE offset;uint32_tremaining bufsize;while(remaining 0) {uint32_tread_size (remaining FLASH_SECTOR_SIZE) ? FLASH_SECTOR_SIZE : remaining;QSPI_FLASH_StandardSPI_FastRead(addr, buffer, read_size);addr read_size;buffer read_size;remaining - read_size;}return(int32_t) bufsize;}8、【重点关注】tud_msc_write10_cb 函数实现tud_msc_write10_cb 是 TinyUSB 协议栈中处理 USB Mass Storage Class (MSC) 写入请求 的核心回调函数负责将主机如 PC/Mac发送的数据写入存储设备如 Flash复制int32_ttud_msc_write10_cb(uint8_tlun,uint32_tlba,uint32_toffset,uint8_t* buffer,uint32_tbufsize){(void) lun;if( lba DISK_BLOCK_NUM )return-1;uint32_taddr lba * DISK_BLOCK_SIZE offset;QSPI_FLASH_SmartEraseThenWrite(addr,buffer,bufsize);return(int32_t) bufsize;}由于flash檫除需要按照扇区(4096)檫除而写入时为避免对原扇区的内容覆盖需要先读取原内容再补充写入内容再按照page(256)写入复制uint8_tsector_buffer[FLASH_SECTOR_SIZE];// 临时扇区缓冲区intQSPI_FLASH_SmartEraseThenWrite(uint32_tAddress,uint8_t*Buffer,uint32_tLength) {#define SECTOR_SIZE (4 * 1024)// 4KB扇区#define PAGE_SIZE 256// 页大小if(Buffer NULL|| Length 0)return-1;uint32_tstart_sector Address / SECTOR_SIZE;uint32_tend_sector (Address Length -1) / SECTOR_SIZE;for(uint32_tsector start_sector; sector end_sector; sector) {uint32_tsector_addr sector * SECTOR_SIZE;uint32_tsector_start (sector start_sector) ? (Address % SECTOR_SIZE) :0;uint32_tsector_end (sector end_sector) ? ((Address Length -1) % SECTOR_SIZE) : (SECTOR_SIZE -1);uint32_tsector_len sector_end - sector_start 1;// 1. 读取整个扇区到缓冲区(如果需要保留未修改部分)// 这里假设需要保留未修改部分所以先读取整个扇区// 如果确定是全新写入可以跳过读取步骤// 实现一个读取函数(您需要提供类似QSPI_FLASH_StandardSPI_Read)QSPI_FLASH_StandardSPI_FastRead(sector_addr, sector_buffer, SECTOR_SIZE);// 2. 修改缓冲区中需要更新的部分uint32_tbuf_offset (sector start_sector) ? (Address % SECTOR_SIZE) :0;uint32_tdata_offset (sector start_sector) ?0: (sector * SECTOR_SIZE - Address);uint32_tcopy_len (Length - data_offset) sector_len ? sector_len : (Length - data_offset);memcpy(sector_buffer buf_offset, Buffer data_offset, copy_len);// 3. 擦除整个扇区QSPI_FLASH_StandardSPI_SectorErase(sector);// 4. 逐页写回整个扇区for(uint32_toffset 0; offset SECTOR_SIZE; offset PAGE_SIZE) {uint32_twrite_size (SECTOR_SIZE - offset) PAGE_SIZE ? PAGE_SIZE : (SECTOR_SIZE - offset);QSPI_FLASH_StandardSPI_PageProgram(sector_addr offset, sector_buffer offset, write_size);}}return0;}9、【重点关注】tud_msc_scsi_cb 函数实现tud_msc_scsi_cb 是 TinyUSB 协议栈中处理 所有未单独实现的 SCSI 命令 的通用回调函数作为 MSCMass Storage Class设备的底层命令处理中枢处理未被 TinyUSB 单独回调函数如read10_cb/write10_cb覆盖的 SCSI 命令包括设备信息查询如 SCSI_INQUIRY介质状态检查如 SCSI_TEST_UNIT_READY模式参数配置如 SCSI_MODE_SENSE_6复制int32_ttud_msc_scsi_cb (uint8_tlun,uint8_tconstscsi_cmd[16],void* buffer,uint16_tbufsize){// read10 write10 has their own callback and MUST not be handled herevoidconst* response NULL;int32_tresplen 0;boolin_xfer true;switch(scsi_cmd[0]) {caseSCSI_CMD_MODE_SENSE_6:case0x5A://SCSI_CMD_MODE_SENSE_10:// 返回相同的模式页数据{staticuint8_tmode_sense_data[] {0x03,0x00,0x00,0x00,// Header// Block descriptor(DISK_BLOCK_NUM 24) 0xFF, (DISK_BLOCK_NUM 16) 0xFF,(DISK_BLOCK_NUM 8) 0xFF, DISK_BLOCK_NUM 0xFF,0x00,0x00,// Reserved(DISK_BLOCK_SIZE 8) 0xFF, DISK_BLOCK_SIZE 0xFF,// Mode page0x1C,0x0A,0x00,0x00,0x00,0x00,0x00,0x00};responsemode_sense_data;resplensizeof(mode_sense_data);}break;caseSCSI_CMD_READ_FORMAT_CAPACITY://0x23// 返回格式容量数据{staticuint8_tread_capacity_data[] {0x00,0x00,0x00,0x08,// Capacity List Length(DISK_BLOCK_NUM 24) 0xFF, (DISK_BLOCK_NUM 16) 0xFF,(DISK_BLOCK_NUM 8) 0xFF, DISK_BLOCK_NUM 0xFF,(DISK_BLOCK_SIZE 8) 0xFF, DISK_BLOCK_SIZE 0xFF,0x02// Formatted Media};response read_capacity_data;resplen sizeof(read_capacity_data);}break;default:tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST,0x20,0x00);resplen -1;break;}if(response (resplen 0)) {if(in_xfer) {memcpy(buffer, response, (size_t) resplen);}}return(resplen bufsize) ? bufsize : resplen;}10、其他参数ffconf.h#define FF_USER_DISK_ENABLE#define FF_USE_MKFS 1#define FF_CODE_PAGE 936#define FF_MIN_SS 4096#define FF_MAX_SS 4096另外根据flash型号#define FLASH_SECTOR_SIZE 4096#define FLASH_BLOCK_SIZE 16#define FLASH_SECTOR_COUNT (4*1024*1024/FLASH_SECTOR_SIZE)11、Tinyusb Device配置复制voidTinyUSB_Device_Configure(void){USB_DeviceClockInit();// init device stack on configured roothub porttud_init(BOARD_TUD_RHPORT);}复制voidTinyUSB_Device_CDC_MSC_Sample(void){printf(\r\nTest %s, __FUNCTION__);TinyUSB_Device_Configure();while(1){tud_task();// TinyUSB任务处理cdc_task();//led_blinking_task();}}五、MCU文件系统操作FATFSUART模式实现1、将以下文件加入工程其中user_diskio.c需要修改为对flash支持2、user_diskio.c需要实现的接口函数DSTATUS USER_GetDiskStatus(BYTE lun)DSTATUS USER_DiskInitialize(BYTE lun)DRESULT USER_DiskRead(BYTE lun, BYTE *buff, DWORD sector, UINT count)DRESULT USER_DiskWrite(BYTE lun, const BYTE *buff, DWORD sector, UINT count)DRESULT USER_DiskIoctl(BYTE lun, BYTE cmd, void *buff)3、DSTATUS USER_GetDiskStatus(BYTE lun)函数实现Get Drive Status复制DSTATUSUSER_GetDiskStatus(BYTE lun){//DSTATUS stat STA_NOINIT;/* Add User Code Begin GetDiskStatus */if(lun !0)returnSTA_NOINIT;return0;// 假设始终就绪实际可检查Flash状态寄存器/* Add User Code End GetDiskStatus *///return stat;}4、DSTATUS USER_DiskInitialize(BYTE lun)函数实现Initialize Disk Drive需要调用QSPI示例中的QSPI_Configure函数。复制DSTATUSUSER_DiskInitialize(BYTE lun){DSTATUS stat STA_NOINIT;/* Add User Code Begin DiskInitialize */if(lun !0)returnSTA_NOINIT;// 仅支持LUN 0// 初始化QSPI接口QSPI_Configure();statRES_OK;/* Add User Code End DiskInitialize */returnstat;}5、USER_DiskRead函数实现Read Sector需要调用QSPI示例中的QSPI_FLASH_StandardSPI_FastRead函数。复制DRESULT USER_DiskRead(BYTE lun, BYTE *buff, DWORD sector,UINTcount){DRESULT res RES_PARERR;/* Add User Code Begin DiskRead */if(lun !0)returnRES_PARERR;for(; count 0; count--){QSPI_FLASH_StandardSPI_FastRead(sector * FLASH_SECTOR_SIZE, buff, FLASH_SECTOR_SIZE);sector;buff FLASH_SECTOR_SIZE;}res RES_OK;/* Add User Code End DiskRead */returnres;}6、USER_DiskWrite函数实现复制DRESULTUSER_DiskWrite(BYTE lun,constBYTE *buff, DWORD sector, UINT count){DRESULT res RES_PARERR;/* Add User Code Begin DiskWrite */if(lun !0)returnRES_PARERR;for(;count0;count--){QSPI_FLASH_Write((uint8_t*)buff,sector*FLASH_SECTOR_SIZE,FLASH_SECTOR_SIZE);sector;buffFLASH_SECTOR_SIZE;}res RES_OK;/* Add User Code End DiskWrite */returnres;}其中QSPI_FLASH_Write复制/*** [urlhome.php?modspaceuid247401]brief[/url] 向 Flash 写入数据自动处理擦除和分页* param pData 要写入的数据指针* param WriteAddr 写入起始地址字节单位* param Size 要写入的字节数* [urlhome.php?modspaceuid536309]NOTE[/url] 基于 W25QXX_Write 逻辑改写适配 QSPI_FLASH_StandardSPI_* 函数*/voidQSPI_FLASH_Write(constuint8_t*pData,uint32_tWriteAddr,uint16_tSize){uint32_tsector_pos;uint16_tsector_offset;uint16_tsector_remain;uint16_ti;uint8_t*pSectorBuf sector_buffer;// 指向扇区缓冲区sector_pos WriteAddr / FLASH_SECTOR_SIZE;// 计算扇区位置sector_offset WriteAddr % FLASH_SECTOR_SIZE;// 扇区内偏移量sector_remain FLASH_SECTOR_SIZE - sector_offset;// 当前扇区剩余空间// 如果请求写入的字节数不超过当前扇区剩余空间则调整实际写入长度if(Size sector_remain) {sector_remain Size;}while(1) {// 1. 读取整个扇区到缓冲区QSPI_FLASH_StandardSPI_FastRead(sector_pos * FLASH_SECTOR_SIZE, pSectorBuf, FLASH_SECTOR_SIZE);// 2. 检查是否需要擦除当前扇区是否有非0xFF数据需要覆盖for(i 0; i sector_remain; i) {if(pSectorBuf[sector_offset i] !0xFF) {break;// 需要擦除}}// 3. 如果需要擦除if(i sector_remain) {// 3.1 擦除整个扇区QSPI_FLASH_StandardSPI_SectorErase(sector_pos);// 3.2 将新数据合并到缓冲区for(i 0; i sector_remain; i) {pSectorBuf[sector_offset i] pData[i];}// 3.3 写回整个扇区QSPI_FLASH_StandardSPI_WriteSector(pSectorBuf, sector_pos * FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE);}else{// 4. 如果不需要擦除直接写入数据QSPI_FLASH_StandardSPI_WriteSector(pData, WriteAddr, sector_remain);}// 5. 判断是否写入完成if(Size sector_remain) {break;// 全部写入完成}else{// 6. 调整指针和地址继续写入剩余数据sector_pos;// 下一个扇区sector_offset 0;// 从扇区起始位置开始pData sector_remain;WriteAddr sector_remain;Size - sector_remain;// 计算下一个扇区要写入的长度sector_remain (Size FLASH_SECTOR_SIZE) ? FLASH_SECTOR_SIZE : Size;}}}QSPI_FLASH_StandardSPI_WriteSector复制/*** [urlhome.php?modspaceuid247401]brief[/url] 写入整个扇区数据自动分页编程* param pData: 要写入的数据指针* param WriteAddr: 写入起始地址需4KB对齐* param Size: 写入字节数必须为FLASH_SECTOR_SIZE* retval 无*/voidQSPI_FLASH_StandardSPI_WriteSector(constuint8_t*pData,uint32_tWriteAddr,uint16_tSize){uint16_tpage_size 256;// Flash页编程大小uint16_tpages Size / page_size;/* 参数检查 */if((WriteAddr % FLASH_SECTOR_SIZE !0) || (Size ! FLASH_SECTOR_SIZE)) {printf(Error: Addr/size not aligned!\r\n);return;}/* 分页写入 */for(uint16_ti 0; i pages; i) {QSPI_FLASH_StandardSPI_PageProgram(WriteAddr (i * page_size),// 目标地址pData (i * page_size),// 数据指针page_size// 固定写入256字节);/* 无需额外等待PageProgram内部已调用WaitBusy */}}7、USER_DiskIoctl函数实现I/O Control复制DRESULT USER_DiskIoctl(BYTE lun, BYTE cmd,void*buff){DRESULT res RES_PARERR;/* Add User Code Begin DiskIoctl */DWORD *pdword NULL;switch(cmd) {caseGET_SECTOR_SIZE: *(WORD*)buff FLASH_SECTOR_SIZE;returnRES_OK;break;// 4KB扇区caseGET_BLOCK_SIZE: *(DWORD*)buff FLASH_BLOCK_SIZE;returnRES_OK;break;// 擦除块1扇区caseGET_SECTOR_COUNT:*(DWORD*)buff FLASH_SECTOR_COUNT;returnRES_OK;break;// 4MB/4KB1024扇区caseCTRL_SYNC:returnRES_OK;break;// 同步操作无实际Flash操作default:returnRES_PARERR;}/* Add User Code End DiskIoctl */returnres;}8、get_fattime函数实现复制// 软件RTC结构体typedefstruct{uint16_tyear;// 年份如2023uint8_tmonth;// 1-12uint8_tday;// 1-31uint8_thour;// 0-23uint8_tmin;// 0-59uint8_tsec;// 0-59} SoftRTC_Time;// 全局变量存储当前时间volatileSoftRTC_Time current_time {.year 2025,.month 6,.day 20,.hour 0,.min 0,.sec 0};DWORDget_fattime(void){return((DWORD)(current_time.year -1980) 25)// 年份1980为基础| ((DWORD)current_time.month 21)// 月份| ((DWORD)current_time.day 16)// 日| ((DWORD)current_time.hour 11)// 小时| ((DWORD)current_time.min 5)// 分钟| ((DWORD)current_time.sec /2);// 秒/2FatFs精度}9、相关参数#define FF_USER_DISK_ENABLE#define FLASH_SECTOR_SIZE 4096#define FLASH_BLOCK_SIZE 16#define FLASH_SECTOR_COUNT (4*1024*1024/FLASH_SECTOR_SIZE)六、UART实现发送FatFs指令1、需要UART不定长接收处理这里没有实现这一部分接收到数据调用Process_Input复制voidUART_Sample(void){init_filesystem();show_welcome();printf(Command processor ready\r\n);USART_RxData_Interrupt(255);while(1){if(1 USART_RxStruct.CompleteFlagUSART_RxStruct.CurrentCount0){USART_RxStruct.CompleteFlag0;Process_Input();}}}2、解析命令复制voidProcess_Input(void){parse_command((char*)USART_RxStruct.Buffer);USART_RxData_Interrupt(255);USART_ITConfig(USART3, USART_IT_IDLE, ENABLE);}复制// 解析并执行命令voidparse_command(char*cmd) {if(strncmp(cmd,\r,1)0) cmdcmd1;//puTTY终端用// 保存到历史记录if(history_count MAX_HISTORY) {strncpy(cmd_history[history_count], cmd, UART_BUF_SIZE-1);history_count;}else{// 滚动历史记录for(inti 0; i MAX_HISTORY-1; i) {strcpy(cmd_history[i], cmd_history[i1]);}strncpy(cmd_history[MAX_HISTORY-1], cmd, UART_BUF_SIZE-1);}// 检查特殊命令if(strncmp(cmd,help,4) 0) {show_help();return;}if(strncmp(cmd,history,7) 0) {show_history();return;}if(strncmp(cmd,clear\r\n,5) 0) {clear_screen();return;}if(strncmp(cmd,dir,3) 0) {handle_dir();return;}// 解析带参数的命令char*token strtok(cmd,,);if(token NULL)return;if(strncmp(token,mkfile,6) 0) {char*filename strtok(NULL,,);char*content strtok(NULL,);if(filename content) {// 跳过可能的空格while(*filename ) filename;while(*content ) content;handle_mkfile(filename, content);}else{printf(Invalid format. Usage: mkfile,filename,content\r\n);}}elseif(strncmp(token,type,4) 0) {char*filename strtok(NULL,);if(filename) {// 跳过可能的空格while(*filename ) filename;handle_type(filename);}else{printf(Invalid format. Usage: type,filename\r\n);}}else{printf(Unknown command: %s\r\n, token);}}3、各种命令处理函数复制// 显示命令历史voidshow_history() {printf(\r\nCommand History:\r\n);for(inti 0; i history_count; i) {printf(%d: %s\r\n, i1, cmd_history[i]);}printf(\r\n);}// 显示帮助信息voidshow_help() {printf(\r\nAvailable Commands:\r\n);printf(----------------------------------------\r\n);printf(mkfile, filename, content - Create file\r\n);printf(dir - List directory\r\n);printf(type, filename - Show file content\r\n);printf(history - Show command history\r\n);printf(clear - Clear screen\r\n);printf(help - Show this help\r\n);printf(----------------------------------------\r\n\r\n);}// 清除屏幕voidclear_screen() {// ANSI转义序列清除屏幕printf(\033[2J\033[H);}// 显示欢迎信息和系统状态voidshow_welcome() {clear_screen();printf(\033[1;34m);// 蓝色printf(\r\n);printf( UART File System Command Processor\r\n);printf(\r\n);printf(\033[0m);// 重置颜色if(fs_status.fs_mounted) {printf(File System: FAT32 | Total: %lu KB | Free: %lu KB\r\n,fs_status.total_space*8, fs_status.free_space*8);}else{printf(File System: NOT MOUNTED\r\n);}printf(----------------------------------------\r\n);printf(Type help for available commands\r\n);printf(\r\n\r\n);}voidEcho_Back(void){USART_TxData_Interrupt((uint8_t*)USART_RxStruct.Buffer, USART_RxStruct.CurrentCount);while(USART_TxStruct.CompleteFlag!1){};}// 初始化文件系统voidinit_filesystem() {res f_mount(fs,,1);// 挂载文件系统if(res ! FR_OK) {printf(Failed to mount filesystem\r\n);}// 获取存储空间信息FATFS *fs_ptr;DWORD fre_clust;res f_getfree(, fre_clust, fs_ptr);if(res FR_OK) {fs_status.total_space (fs_ptr-n_fatent -2) * fs_ptr-csize /2;// KBfs_status.free_space fre_clust * fs_ptr-csize /2;// KBfs_status.fs_mounted 1;}}// 处理mkfile命令voidhandle_mkfile(char*filename,char*content) {res f_open(file, filename, FA_WRITE | FA_CREATE_ALWAYS);if(res ! FR_OK) {printf(Failed to create file\r\n);return;}UINT bytes_written;res f_write(file, content,strlen(content), bytes_written);if(res ! FR_OK || bytes_written !strlen(content)) {printf(Failed to write file\r\n);}else{printf(File created successfully\r\n);}f_close(file);}// 处理dir命令voidhandle_dir() {DIR dir;FILINFO fno;uint32_ttotal_files 0;uint32_ttotal_size 0;res f_opendir(dir,/);if(res ! FR_OK) {printf(Error opening directory: %d\r\n, res);return;}printf(\r\nDirectory Listing:\r\n);printf(----------------------------------------\r\n);printf(Name Size Date Time\r\n);printf(----------------------------------------\r\n);while(1) {res f_readdir(dir, fno);if(res ! FR_OK || fno.fname[0] 0)break;if(fno.fattrib AM_DIR) {// 目录printf([%s] DIR , fno.fname);}else{// 文件printf(%-24s %8lu , fno.fname, fno.fsize);total_files;total_size fno.fsize;}// 显示日期和时间printf(%04d-%02d-%02d %02d:%02d\r\n,(fno.fdate 9) 1980,(fno.fdate 5) 0x0F,fno.fdate 0x1F,fno.ftime 11,(fno.ftime 5) 0x3F);}f_closedir(dir);printf(----------------------------------------\r\n);printf(%d file(s), %lu bytes\r\n, total_files, total_size*8);printf(Free space: %lu KB\r\n\r\n, fs_status.free_space*8);}// 处理type命令voidhandle_type(char*filename) {// 移除文件名可能存在的引号if(filename[0] filename[strlen(filename)-1] ) {memmove(filename, filename1,strlen(filename)-2);filename[strlen(filename)-2] \0;}res f_open(file, filename, FA_READ);if(res ! FR_OK) {printf(Error opening file: %d\r\n, res);return;}printf(\r\nContents of %s:\r\n, filename);printf(----------------------------------------\r\n);charbuffer[128];UINT bytes_read;UINT total_read 0;while(1) {res f_read(file, buffer,sizeof(buffer) -1, bytes_read);if(res ! FR_OK || bytes_read 0)break;buffer[bytes_read] \0;printf(buffer);total_read bytes_read;}f_close(file);printf(\r\n----------------------------------------\r\n);printf(%u bytes read\r\n\r\n, total_read);}七、运行https://www.bilibili.com/video/BV1xfK7ztEwG/?vd_source5b0f94f2f57c38a43471771787964a99。---------------------作者sujingliang链接https://bbs.21ic.com/icview-3464696-1-1.html来源21ic.com此文章已获得原创/原创奖标签著作权归21ic所有任何人未经允许禁止转载。