- int main(void)
- {
- PLATFORM_Init(); //板级驱动,不用驱动uart3
- USART_Configure(115200); //UART3驱动
- EXTI_Configure(); //key1(PB0),key2(PB1)初始化
-
- if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0){ //判断PB0电平
- modeChangeRequest=1; //如果低电平,设置模式1
- }else{
- modeChangeRequest=0; //如果低电平,设置模式0
- }
- if (modeChangeRequest==1) { //模式1,USB存储(TinyUSB)模式
- printf("Enter Usb Msc Mode\r\n");
- QSPI_Configure(); //QSPI初始化,驱动外部flash
- TinyUSB_Device_CDC_MSC_Sample(); //进入tiny初始化和循环
- }
- else //模式0,MCU文件系统操作(FATFS+UART)模式
- {
- 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)提供存储设备的基本标识信息。
- void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4])
- {
- (void) lun;
- const char vid[] = "TinyUSB";
- const char pid[] = "Mass Storage";
- const char rev[] = "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)报告存储设备当前是否就绪并可访问。
- bool tud_msc_test_unit_ready_cb(uint8_t lun)
- {
- (void) lun;
- // RAM disk is ready until ejected
- if (ejected) {
- // Additional Sense 3A-00 is NOT_FOUND
- tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00);
- return false;
- }
- return true;
- }
5、tud_msc_capacity_cb函数实现
tud_msc_capacity_cb 是 TinyUSB 协议栈中用于响应 USB Mass Storage Class (MSC) 容量查询请求 的核心回调函数,其作用是向主机(如 PC/Mac)报告存储设备的物理容量参数。
- void tud_msc_capacity_cb(uint8_t lun, 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) 启停事件 的关键回调函数,主要用于响应主机的设备加载/弹出指令和电源状态管理
- bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject)
- {
- (void) lun;
- (void) power_condition;
- if ( load_eject )
- {
- if (start)
- {
- // load disk storage
- ejected = false;
- }else
- {
- // unload disk storage
- ejected = true;
- }
- }
- return true;
- }
7、【重点关注】int32_t tud_msc_read10_cb函数实现
tud_msc_read10_cb 是 TinyUSB 协议栈中处理 USB Mass Storage Class (MSC) 读取请求 的核心回调函数,负责将存储设备的数据通过 USB 传输给主机
- int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)
- {
- (void) lun;
- //uint32_t addr = lba * DISK_BLOCK_SIZE + offset;
- // out of ramdisk
- if ( lba >= DISK_BLOCK_NUM ) return -1;
- uint32_t addr = lba * FLASH_SECTOR_SIZE + offset;
- uint32_t remaining = bufsize;
-
- while (remaining > 0) {
- uint32_t read_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_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize)
- {
- (void) lun;
- if ( lba >= DISK_BLOCK_NUM ) return -1;
- uint32_t addr = lba * DISK_BLOCK_SIZE + offset;
- QSPI_FLASH_SmartEraseThenWrite(addr,buffer,bufsize);
- return (int32_t) bufsize;
- }
由于flash檫除需要按照扇区(4096)檫除,而写入时为避免对原扇区的内容覆盖需要先读取原内容再补充写入内容,再按照page(256)写入
- uint8_t sector_buffer[FLASH_SECTOR_SIZE]; // 临时扇区缓冲区
- int QSPI_FLASH_SmartEraseThenWrite(uint32_t Address, uint8_t *Buffer, uint32_t Length) {
- #define SECTOR_SIZE (4 * 1024) // 4KB扇区
- #define PAGE_SIZE 256 // 页大小
-
- if (Buffer == NULL || Length == 0) return -1;
-
-
- uint32_t start_sector = Address / SECTOR_SIZE;
- uint32_t end_sector = (Address + Length - 1) / SECTOR_SIZE;
-
- for (uint32_t sector = start_sector; sector <= end_sector; sector++) {
- uint32_t sector_addr = sector * SECTOR_SIZE;
- uint32_t sector_start = (sector == start_sector) ? (Address % SECTOR_SIZE) : 0;
- uint32_t sector_end = (sector == end_sector) ? ((Address + Length - 1) % SECTOR_SIZE) : (SECTOR_SIZE - 1);
- uint32_t sector_len = sector_end - sector_start + 1;
-
- // 1. 读取整个扇区到缓冲区(如果需要保留未修改部分)
- // 这里假设需要保留未修改部分,所以先读取整个扇区
- // 如果确定是全新写入,可以跳过读取步骤
-
- // 实现一个读取函数(您需要提供类似QSPI_FLASH_StandardSPI_Read)
- QSPI_FLASH_StandardSPI_FastRead(sector_addr, sector_buffer, SECTOR_SIZE);
-
- // 2. 修改缓冲区中需要更新的部分
- uint32_t buf_offset = (sector == start_sector) ? (Address % SECTOR_SIZE) : 0;
- uint32_t data_offset = (sector == start_sector) ? 0 : (sector * SECTOR_SIZE - Address);
- uint32_t copy_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_t offset = 0; offset < SECTOR_SIZE; offset += PAGE_SIZE) {
- uint32_t write_size = (SECTOR_SIZE - offset) > PAGE_SIZE ? PAGE_SIZE : (SECTOR_SIZE - offset);
- QSPI_FLASH_StandardSPI_PageProgram(sector_addr + offset, sector_buffer + offset, write_size);
- }
- }
-
- return 0;
- }
9、【重点关注】tud_msc_scsi_cb 函数实现
tud_msc_scsi_cb 是 TinyUSB 协议栈中处理 所有未单独实现的 SCSI 命令 的通用回调函数,作为 MSC(Mass Storage Class)设备的底层命令处理中枢
处理未被 TinyUSB 单独回调函数(如read10_cb/write10_cb)覆盖的 SCSI 命令,包括:
设备信息查询(如 SCSI_INQUIRY)
介质状态检查(如 SCSI_TEST_UNIT_READY)
模式参数配置(如 SCSI_MODE_SENSE_6)
- int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize)
- {
- // read10 & write10 has their own callback and MUST not be handled here
- void const* response = NULL;
- int32_t resplen = 0;
- bool in_xfer = true;
- switch (scsi_cmd[0]) {
- case SCSI_CMD_MODE_SENSE_6:
-
- case 0x5A://SCSI_CMD_MODE_SENSE_10:
- // 返回相同的模式页数据
- {
- static uint8_t mode_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 page
- 0x1C, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- };
- response=mode_sense_data;
- resplen= sizeof(mode_sense_data);
- }
- break;
-
- case SCSI_CMD_READ_FORMAT_CAPACITY: //0x23
- // 返回格式容量数据
- {
- static uint8_t read_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配置
- void TinyUSB_Device_Configure(void)
- {
- USB_DeviceClockInit();
- // init device stack on configured roothub port
- tud_init(BOARD_TUD_RHPORT);
- }
- void TinyUSB_Device_CDC_MSC_Sample(void)
- {
- printf("\r\nTest %s", __FUNCTION__);
-
- TinyUSB_Device_Configure();
-
- while (1)
- {
- tud_task(); // TinyUSB任务处理
- cdc_task();
- //led_blinking_task();
- }
- }
五、MCU文件系统操作(FATFS+UART)模式实现
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
- DSTATUS USER_GetDiskStatus(BYTE lun)
- {
- //DSTATUS stat = STA_NOINIT;
- /* Add User Code Begin GetDiskStatus */
- if (lun != 0) return STA_NOINIT;
- return 0; // 假设始终就绪(实际可检查Flash状态寄存器)
- /* Add User Code End GetDiskStatus */
- //return stat;
- }
4、DSTATUS USER_DiskInitialize(BYTE lun)函数实现
Initialize Disk Drive
需要调用QSPI示例中的QSPI_Configure函数。
- DSTATUS USER_DiskInitialize(BYTE lun)
- {
- DSTATUS stat = STA_NOINIT;
- /* Add User Code Begin DiskInitialize */
- if (lun != 0) return STA_NOINIT; // 仅支持LUN 0
-
- // 初始化QSPI接口
- QSPI_Configure();
- stat=RES_OK;
-
- /* Add User Code End DiskInitialize */
- return stat;
- }
5、USER_DiskRead函数实现
Read Sector
需要调用QSPI示例中的QSPI_FLASH_StandardSPI_FastRead函数。
- DRESULT USER_DiskRead(BYTE lun, BYTE *buff, DWORD sector, UINT count)
- {
- DRESULT res = RES_PARERR;
- /* Add User Code Begin DiskRead */
- if (lun != 0) return RES_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 */
- return res;
- }
6、USER_DiskWrite函数实现
- DRESULT USER_DiskWrite(BYTE lun, const BYTE *buff, DWORD sector, UINT count)
- {
- DRESULT res = RES_PARERR;
- /* Add User Code Begin DiskWrite */
- if (lun != 0) return RES_PARERR;
- for(;count>0;count--)
- {
- QSPI_FLASH_Write((uint8_t*)buff,sector*FLASH_SECTOR_SIZE,FLASH_SECTOR_SIZE);
- sector++;
- buff+=FLASH_SECTOR_SIZE;
- }
- res= RES_OK;
- /* Add User Code End DiskWrite */
- return res;
- }
其中,QSPI_FLASH_Write
- /**
- * [url=home.php?mod=space&uid=247401]@brief[/url] 向 Flash 写入数据(自动处理擦除和分页)
- * @param pData 要写入的数据指针
- * @param WriteAddr 写入起始地址(字节单位)
- * @param Size 要写入的字节数
- * [url=home.php?mod=space&uid=536309]@NOTE[/url] 基于 W25QXX_Write 逻辑改写,适配 QSPI_FLASH_StandardSPI_* 函数
- */
- void QSPI_FLASH_Write(const uint8_t *pData, uint32_t WriteAddr, uint16_t Size)
- {
- uint32_t sector_pos;
- uint16_t sector_offset;
- uint16_t sector_remain;
- uint16_t i;
- 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:
- /**
- * [url=home.php?mod=space&uid=247401]@brief[/url] 写入整个扇区数据(自动分页编程)
- * @param pData: 要写入的数据指针
- * @param WriteAddr: 写入起始地址(需4KB对齐)
- * @param Size: 写入字节数(必须为FLASH_SECTOR_SIZE)
- * @retval 无
- */
- void QSPI_FLASH_StandardSPI_WriteSector(const uint8_t *pData, uint32_t WriteAddr, uint16_t Size)
- {
- uint16_t page_size = 256; // Flash页编程大小
- uint16_t pages = Size / page_size;
-
- /* 参数检查 */
- if((WriteAddr % FLASH_SECTOR_SIZE != 0) || (Size != FLASH_SECTOR_SIZE)) {
- printf("Error: Addr/size not aligned!\r\n");
- return;
- }
- /* 分页写入 */
- for(uint16_t i = 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) {
- case GET_SECTOR_SIZE: *(WORD*)buff = FLASH_SECTOR_SIZE; return RES_OK; break; // 4KB扇区
- case GET_BLOCK_SIZE: *(DWORD*)buff = FLASH_BLOCK_SIZE; return RES_OK; break; // 擦除块=1扇区
- case GET_SECTOR_COUNT:
- *(DWORD*)buff= FLASH_SECTOR_COUNT;
- return RES_OK; break; // 4MB/4KB=1024扇区
- case CTRL_SYNC: return RES_OK; break; // 同步操作(无实际Flash操作)
- default: return RES_PARERR;
- }
- /* Add User Code End DiskIoctl */
- return res;
- }
8、get_fattime函数实现
- // 软件RTC结构体
- typedef struct {
- uint16_t year; // 年份(如2023)
- uint8_t month; // 1-12
- uint8_t day; // 1-31
- uint8_t hour; // 0-23
- uint8_t min; // 0-59
- uint8_t sec; // 0-59
- } SoftRTC_Time;
- // 全局变量存储当前时间
- volatile SoftRTC_Time current_time = {
- .year = 2025,
- .month = 6,
- .day = 20,
- .hour = 0,
- .min = 0,
- .sec = 0
- };
- DWORD get_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); // 秒/2(FatFs精度)
- }
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()
- void UART_Sample(void)
- {
-
- init_filesystem();
- show_welcome();
- printf("Command processor ready\r\n");
-
- USART_RxData_Interrupt(255);
- while(1)
- {
-
- if (1== USART_RxStruct.CompleteFlag&&USART_RxStruct.CurrentCount>0)
- {
- USART_RxStruct.CompleteFlag=0;
- Process_Input();
-
- }
- }
- }
-
2、解析命令
- void Process_Input(void)
- {
- parse_command((char*)USART_RxStruct.Buffer);
- USART_RxData_Interrupt(255);
- USART_ITConfig(USART3, USART_IT_IDLE, ENABLE);
- }
- // 解析并执行命令
- void parse_command(char *cmd) {
-
- if(strncmp(cmd,"\r",1)==0) cmd=cmd+1; //puTTY终端用
- // 保存到历史记录
- if (history_count < MAX_HISTORY) {
- strncpy(cmd_history[history_count], cmd, UART_BUF_SIZE-1);
- history_count++;
- } else {
- // 滚动历史记录
- for (int i = 0; i < MAX_HISTORY-1; i++) {
- strcpy(cmd_history[i], cmd_history[i+1]);
- }
- 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");
- }
- }
-
- else if (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、各种命令处理函数
- // 显示命令历史
- void show_history() {
- printf("\r\nCommand History:\r\n");
- for (int i = 0; i < history_count; i++) {
- printf("%d: %s\r\n", i+1, cmd_history[i]);
- }
- printf("\r\n");
- }
- // 显示帮助信息
- void show_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");
- }
- // 清除屏幕
- void clear_screen() {
- // ANSI转义序列清除屏幕
- printf("\033[2J\033[H");
- }
- // 显示欢迎信息和系统状态
- void show_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");
- }
- void Echo_Back(void)
- {
- USART_TxData_Interrupt((uint8_t *)USART_RxStruct.Buffer, USART_RxStruct.CurrentCount);
- while(USART_TxStruct.CompleteFlag!=1){};
- }
- // 初始化文件系统
- void init_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; // KB
- fs_status.free_space = fre_clust * fs_ptr->csize / 2; // KB
- fs_status.fs_mounted = 1;
- }
- }
- // 处理mkfile命令
- void handle_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命令
- void handle_dir() {
- DIR dir;
- FILINFO fno;
- uint32_t total_files = 0;
- uint32_t total_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命令
- void handle_type(char *filename) {
- // 移除文件名可能存在的引号
- if (filename[0] == '"' && filename[strlen(filename)-1] == '"') {
- memmove(filename, filename+1, 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");
-
- char buffer[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_source=5b0f94f2f57c38a43471771787964a99