你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【福利三:逢7发帖赢大礼】6、基于STM32G070板子spiflash的FATFS移植

[复制链接]
STMWoodData 发布时间:2026-7-19 23:13

下面利用板子上有的SPIFLASH和TF接口,准备实现文件读写控制。之前已经实现了spiflash的sfud库移植,可以驱动SPIFLASH读写数据了。接下来实现基于SPIFLASH的FATFS文件系统移植。

首先打开STM32CubeMX,配置中间件FATFS库参数。

image.png

然后生成KEIL代码工程,在user_diskio.c中实现文件初始化,读写接口函数。就是实现 FatFS 的底层磁盘I/O接口(diskio.c),把这些接口函数与 sfud 的API对接起来。image.png

diskio.c 文件顶部,包含 sfud 的头文件,并定义一个指向你的SPI Flash设备的全局指针。再就是几个接口函数:

实现 disk_initialize (磁盘初始化)

DSTATUS USER_initialize (
    BYTE pdrv           /* Physical drive nmuber to identify the drive */
)
{
  /* USER CODE BEGIN INIT */
    Stat = STA_NOINIT;
    switch (pdrv) {
        case SPI_FLASH:
            // 如果sfud还没有初始化,在这里调用 sfud_init()
            sfud_init();
            g_flash_ptr = (sfud_flash *)sfud_get_device_table();
            // 可以在这里读取设备ID来验证是否成功
            if (sfud_get_device_num() > 0) {
                Stat &= ~STA_NOINIT; // 清除“未初始化”标志
            }
            break;
        // ... 其他设备

    }
    return Stat;
  /* USER CODE END INIT */
}

实现 disk_status (获取磁盘状态)

DSTATUS USER_status (
    BYTE pdrv       /* Physical drive number to identify the drive */
)
{
  /* USER CODE BEGIN STATUS */
    Stat = STA_NOINIT;
    switch (pdrv) {
        case SPI_FLASH:
            // 简单实现:假设只要sfud能读到设备ID,就认为状态OK
            if (sfud_get_device_num() > 0) {
                Stat &= ~STA_NOINIT;
            }
            break;
    }
    return Stat;
  /* USER CODE END STATUS */
}

实现 disk_read (读扇区)

DRESULT USER_read (
    BYTE pdrv,      /* Physical drive nmuber to identify the drive */
    BYTE *buff,     /* Data buffer to store read data */
    DWORD sector,   /* Sector address in LBA */
    UINT count      /* Number of sectors to read */
)
{
  /* USER CODE BEGIN READ */
    switch (pdrv) {
        case SPI_FLASH:
            // sfud_read 的参数是 (设备, 起始地址, 数据缓冲区, 数据大小)
            // 注意:FatFS的扇区地址需要转换为字节地址
            if (sfud_read(g_flash_ptr, sector * 512, count * 512, buff) == SFUD_SUCCESS) {
                return RES_OK;
            } else {
                return RES_ERROR;
            }
        // ... 其他设备
    }
    return RES_OK;
  /* USER CODE END READ */
}

实现 disk_write (写扇区)

uint8_t     work_buffer[4100];
#define     ERASE_BLOCK_SIZE    4096    // 4KB,可根据 Flash 型号调整
int sfud_write_safe(const sfud_flash *flash, uint32_t addr, size_t len, const uint8_t *buff)
{
    uint32_t start_block = addr / ERASE_BLOCK_SIZE;
    uint32_t end_block   = (addr + len - 1) / ERASE_BLOCK_SIZE;
    uint32_t data_offset = 0; // 用户数据偏移

    for (uint32_t block = start_block; block <= end_block; block++) 
    {
        uint32_t block_addr = block * ERASE_BLOCK_SIZE;
        uint32_t offset_start = (block == start_block) ? (addr - block_addr) : 0;
        uint32_t offset_end   = (block == end_block) ? (addr + len - block_addr) : ERASE_BLOCK_SIZE;
        uint32_t block_data_len = offset_end - offset_start;

        // 如果写入范围覆盖整个块,直接擦除+写
        if (offset_start == 0 && offset_end == ERASE_BLOCK_SIZE) {
            if (sfud_erase(flash, block_addr, ERASE_BLOCK_SIZE) != SFUD_SUCCESS)  return RES_ERROR;
            if (sfud_write(flash, block_addr, block_data_len, buff + data_offset) != SFUD_SUCCESS)    return RES_ERROR;
        } else {
            // 读-修改-写
            if (sfud_read(flash, block_addr, ERASE_BLOCK_SIZE, work_buffer) != SFUD_SUCCESS)  return RES_ERROR;
            memcpy(work_buffer + offset_start, buff + data_offset, block_data_len);
            if (sfud_erase(flash, block_addr, ERASE_BLOCK_SIZE) != SFUD_SUCCESS)  return RES_ERROR;
            if (sfud_write(flash, block_addr, ERASE_BLOCK_SIZE, work_buffer) != SFUD_SUCCESS) return RES_ERROR;
        }
        data_offset += block_data_len;
    }
    return RES_OK;
}

#if _USE_WRITE == 1
DRESULT USER_write (
    BYTE pdrv,          /* Physical drive nmuber to identify the drive */
    const BYTE *buff,   /* Data to be written */
    DWORD sector,       /* Sector address in LBA */
    UINT count          /* Number of sectors to write */
)
{
  /* USER CODE BEGIN WRITE */
  /* USER CODE HERE */
    switch (pdrv) {
        case SPI_FLASH: 
            return  sfud_write_safe(g_flash_ptr, sector * 512, count * 512, buff);
        // ... 其他设备

    }
    return RES_PARERR;
  /* USER CODE END WRITE */
}
#endif /* _USE_WRITE == 1 */

实现 disk_ioctl (控制函数)

#if _USE_IOCTL == 1
DRESULT USER_ioctl (
    BYTE pdrv,      /* Physical drive nmuber (0..) */
    BYTE cmd,       /* Control code */
    void *buff      /* Buffer to send/receive control data */
)
{
  /* USER CODE BEGIN IOCTL */
    DRESULT res = RES_ERROR;
    switch (pdrv) {
        case SPI_FLASH:
            switch (cmd) {
                    /* Make sure that no pending write process */
                case CTRL_SYNC :
                    res = RES_OK;
                    break;
                case GET_SECTOR_SIZE:
                    *(WORD*)buff = 512; // 返回扇区大小
                    break;
                case GET_SECTOR_COUNT:
                    // 返回总扇区数 = 总容量 / 扇区大小
                    *(DWORD*)buff = g_flash_ptr->chip.capacity / 512;
                    break;
                case GET_BLOCK_SIZE:
                    *(DWORD*)buff = 4096/512; // 擦除块大小
                    break;
                default:
                    return RES_PARERR;
            }
            return RES_OK;
        // ... 其他设备

    }
    return res;
  /* USER CODE END IOCTL */
}

完成上述移植后,你就可以在应用代码中调用FatFS的标准API来操作文件系统了。

下面就是测试文件系统是否正常运行了。

FATFS       fs;         /* Work area (file system object) for logical drive */
char cur_path[256]; //当前路径

int disk_total_size(char* path)
{
    FATFS       *fs;
    DWORD       fre_clust;
    FRESULT     res;

    res = f_getfree(path, &fre_clust, &fs); 
    if ( res==FR_OK )
    {
        /* Print free space in unit of MB (assuming 512 bytes/sector) */
        printf("\r\nFatfs system:%u KB total drive space. %u KB available.\r\n",
           (int)(((fs->n_fatent - 2) * fs->csize ) / 2 ) , (int)((fre_clust * fs->csize) / 2  ));
        return ENABLE;
    }else     return DISABLE;
}


//====================================================
void fatfs_test_demo(void)
{
    FRESULT     res;
    FIL         fsrc;         /* file objects */
    UINT        br;
    const uint8_t textFileBuffer[] = "Thank you for using Development Board ^_^ \r\n";

    res = f_mount(&fs,"0:",1);
    printf("f_mount disk0 res=%d!\r\n",res);
    if(res == FR_NO_FILESYSTEM)
    {
        sfud_chip_erase((sfud_flash *)sfud_get_device_table());
        res = f_mkfs("0:",FM_FAT,4096,work_buffer,4096);
        printf("f_mkfs disk0 res=%d!\r\n",res);
        res = f_mount(&fs,"0:",1);
        printf("f_mount disk0 res2=%d!\r\n",res);
    }
    if(res == 0)
    {
        res = f_open( &fsrc , "0:/demo.txt" , FA_OPEN_APPEND | FA_WRITE | FA_READ);
        if ( res == FR_OK )
        {
            /* Write buffer to file */
            res = f_write(&fsrc, textFileBuffer, sizeof(textFileBuffer), &br);
            printf("Demo.TXT successfully created        \r\n");
        }else if ( res == FR_EXIST )
        {
            /* Write buffer to file */
            res = f_write(&fsrc, textFileBuffer, sizeof(textFileBuffer), &br);
            printf("Demo.TXT created in the disk1      \r\n");
        }else
        {
            printf("Demo.TXT created in the disk1 error %d.\r\n",res);
        }
        /*close file */
        f_close(&fsrc);

        cur_path[0]='0';
        cur_path[1]=':';
        cur_path[2]='/';
        cur_path[3]= 0;
        disk_total_size(cur_path);
    }
}

在mian函数调用测试函数 void fatfs_test_demo(void) 了。

image.png

编译烧写之后就可以看到打印日志记录了。

image.png

收藏 评论0 发布时间:2026-7-19 23:13

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版