4.5.2 CUBEMX USB之MSC+FATFS+FLASH(内部)

使用工具型号

  • CUBEMX5.6
  • STM32F103ZET6开发板(正点原子战舰)
  • LINK及其他线束
  • STM32Cube_FW_F1 V1.8.0

1.8.1版本代码只使用DEVICE的时候会出现缺少 发送/接收 函数的实体的错误,1.8.1的包里面的例程也有这个错误,目前不知道如何修改,所以使用了1.8.0

1、CUBEMX初始化

  • 初始化RCC

  • 初始化SYS

  • 初始化USART1

  • 初始化USB

  • 初始化FATFS

  • 初始化USB_DEVICE

2、添加文件

这个是在网上找的一个flash读写文件.

/*
 * flash.c
 *
 * Created: 2018-01-29
 * Author: zhanglifu
 */

#include "flash.h"


// 不检查的写入
// WriteAddr:起始地址
// pBuffer:  数据指针
// NumToWrite:字节数数
void FlashWriteNoCheck(uint32_t WriteAddr, uint8_t *pBuffer, uint16_t NumToWrite)
{
    uint16_t i;

    for(i = 0; i < NumToWrite; i += 4)
    {
        while(HAL_OK != HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, WriteAddr + i, *(uint32_t *)(pBuffer + i)));
    }

}

extern void FLASH_PageErase(uint32_t PageAddress);
void FlashWriteBuff(const uint32_t destination_address, uint8_t *const buffer, uint32_t length)
{
    uint16_t i;
    uint8_t FlashBuff[FMC_SECTOR_SIZE];
    uint32_t StartAddress = destination_address - destination_address % FMC_SECTOR_SIZE;
    uint16_t Offset = destination_address - StartAddress;
    uint8_t *pBuf = buffer;
    uint32_t remaindNum = length;

    HAL_StatusTypeDef status = HAL_ERROR;

    // 地址检查
    if((destination_address < FMC_FLASH_BASE) || (destination_address + length >= FMC_FLASH_END) || (length <= 0))
        return;


    HAL_FLASH_Unlock();	// 解锁
    do
    {
        // 读出一页数据
        for(i = 0; i < FMC_SECTOR_SIZE; i += 4)
            * (uint32_t *)(FlashBuff + i) = *(uint32_t *)(StartAddress + i);

        // 修改要改入的数据
        for(i = 0; (i + Offset) < FMC_SECTOR_SIZE && i < remaindNum; i++)
            *(FlashBuff + Offset + i) = *(pBuf + i);


        // 擦除一ROW数据
        FLASH_PageErase(StartAddress);

        // HAL库 FLASH_PageErase有BUFF,要加上下面三行代码
        while(status != HAL_OK)
            status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
        CLEAR_BIT(FLASH->CR, FLASH_CR_PER);

        // 写入数据
        FlashWriteNoCheck(StartAddress, FlashBuff, FMC_SECTOR_SIZE);

        // 为下一页做准备
        StartAddress +=  FMC_SECTOR_SIZE;
        remaindNum -= i;
        pBuf += i;
        Offset = 0;

    }
    while(remaindNum > 0);

    HAL_FLASH_Lock();  // 上锁

}








// 从FLASH中读指定长度数据
void FlashReadBuff(const uint32_t source_address, uint8_t *const buffer, uint16_t length)
{
    uint16_t i;
    uint8_t Offset = 0;
    uint32_t StartAddress = source_address;
    uint16_t data;

    // 地址检测
    if(source_address + length > FMC_FLASH_END) return;

    // 如果没有对16齐
    if(source_address & 1)
    {
        Offset = 1;
        StartAddress = source_address - 1;
    }

    // flash的操作要求16对齐 最小读写操作16个比特
    if(Offset)
    {
        data = *(uint16_t *)(StartAddress);
        buffer[0] = data >> 8;
        StartAddress += 2;
    }

    for(i = 0; i < (length - Offset); i += 2)
    {
        data = *(uint16_t *)(StartAddress + i);
        buffer[i + Offset] = (data & 0xFF);
        if((i + Offset) < (length - 1))
            buffer[i + Offset + 1] = (data >> 8);
    }

}

/*
 * flash.h
 *
 * Created: 2016-04-22
 * Author: zhanglifu
 */
#ifndef _flash_H_
#define _flash_H_


#include "stm32f1xx_hal.h"


/*********************************************************************/
//                        变量定义
/*********************************************************************/
#define STM32_FLASH_BASE 0x08000000 	//STM32 FLASH的起始地址

#define FMC_FLASH_BASE      0x08000000 	// FLASH的起始地址
#define FMC_FLASH_END       0x08080000  // FLASH的结束地址 64


#define DEVICE_INFO_SIZE     0x0400      // 设备信息大小 1K 
#define DEVICE_INFO_ADDRESS  (FMC_FLASH_BASE+(100*1024))   // 设备信息起始地址
#define DEVICE_LOG_ADDRESS   0x08008000  // 设备日志起始地址

void FlashReadBuff(const uint32_t source_address, uint8_t *const buffer, uint16_t length);
void FlashWriteBuff(const uint32_t destination_address, uint8_t *const buffer, uint32_t length);
#endif

3、修改usbd_storage_if.c

  • 修改宏定义
#define STORAGE_LUN_NBR                  1
#define STORAGE_BLK_NBR                  ((100*1024)/STORAGE_BLK_SIZ) //0x10000
#define STORAGE_BLK_SIZ                  (2048/STORAGE_BLOCK_SIZE)
#define STORAGE_BLOCK_SIZE   	         (4)     	    
  • 修改读函数
int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
    /* USER CODE BEGIN 6 */
    uint32_t addr = blk_addr * STORAGE_BLK_SIZ;
    FlashReadBuff(DEVICE_INFO_ADDRESS + addr, buf, blk_len * STORAGE_BLK_SIZ);
    return (USBD_OK);
    /* USER CODE END 6 */
}
  • 修改写函数
int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
    /* USER CODE BEGIN 7 */
    uint32_t addr = blk_addr * STORAGE_BLK_SIZ;
    FlashWriteBuff(DEVICE_INFO_ADDRESS + addr, buf, blk_len * STORAGE_BLK_SIZ);
    return (USBD_OK);
    /* USER CODE END 7 */
}

4、修改user_diskio.c

添加宏定义如下

#define FLASH_SECTOR_SIZE 	    (2048/FLASH_BLOCK_SIZE)
#define  FLASH_SECTOR_COUNT    ((100*1024)/FLASH_SECTOR_SIZE)//12的单位是MB
#define FLASH_BLOCK_SIZE   	    (4)     	    
  • 修改USER_initializeUSER_status
DSTATUS USER_initialize (
	BYTE pdrv           /* Physical drive nmuber to identify the drive */
)
{
  /* USER CODE BEGIN INIT */
    Stat = STA_NOINIT;
    Stat &= ~STA_NOINIT;
    return Stat ;
  /* USER CODE END INIT */
}
DSTATUS USER_status (
	BYTE pdrv       /* Physical drive number to identify the drive */
)
{
  /* USER CODE BEGIN STATUS */
    if(pdrv != 0)
        Stat = STA_NOINIT;
    else
        Stat &= ~STA_NOINIT;
    return Stat;
  /* USER CODE END STATUS */
}
  • 修改读函数
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 */
    DRESULT res = RES_ERROR;
    if(!count)return res; 
    uint32_t addr = sector * FLASH_SECTOR_SIZE;
    FlashReadBuff(DEVICE_INFO_ADDRESS + addr, buff, count*FLASH_SECTOR_SIZE);
    res = RES_OK;
}
  • 修改写函数
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 */
    DRESULT res = RES_ERROR;
    if(!count)return res; //count不能等于0,否则返回参数错误

    uint32_t addr = sector * FLASH_SECTOR_SIZE;
    FlashWriteBuff(DEVICE_INFO_ADDRESS + addr,(uint8_t *const) buff, count*FLASH_SECTOR_SIZE);
    res = RES_OK;
    return res;
  /* USER CODE HERE */
  /* USER CODE END WRITE */
}
  • 修改控制函数
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(cmd)
        {
        case CTRL_SYNC:
            res = RES_OK;
            break;
//获取磁盘的扇区大小 只用于f_mkfs
        case GET_SECTOR_SIZE:
            *(WORD*)buff = FLASH_SECTOR_SIZE;
            res = RES_OK;
            break;
//获取擦除块的大小 只用于f_mkfs  以扇区为单位
        case GET_BLOCK_SIZE:
            *(WORD*)buff = FLASH_BLOCK_SIZE;

            res = RES_OK;
            break;
//获取可利用的扇区大小,MAX_SS>= 1024可用
        case GET_SECTOR_COUNT:
            *(DWORD*)buff = FLASH_SECTOR_COUNT;
            res = RES_OK;
            break;

        default:
            res = RES_PARERR;
            break;
        }   
    return res;
  /* USER CODE END IOCTL */
}

5、添加测试文件

自己写了一个测试用的文件,不是必需品,只是为了方便测试.

#include "ex_fatfs.h"
#include "fatfs.h"
#include "ff_gen_drv.h"
#include <stdio.h>
#include <string.h>

FRESULT  g_res;
FATFS fs;
FIL file;
void fatfs_register()
{
    g_res = f_mount(&fs, "0:", 1);
    if(g_res == FR_NO_FILESYSTEM)
    {
        printf("no file systems\r\n");
        g_res = f_mkfs("0:", 1, 2048);
        if(g_res != FR_OK)
            printf("mkfs_failed err code = %d\r\n",g_res);
        else
            printf("init file systems ok\r\n");
    }
    else if(g_res != FR_OK)
    {
        printf("f_mount failed err code = %d\r\n",g_res);
    }
    else
    {
        printf("flash have file systems\r\n");
    }
}
void fatfs_unregister()
{
    g_res = f_mount(NULL, "0:", 1);
    if(g_res != FR_OK)
    {
        printf("file systems unregister failed err code = %d\r\n",g_res);
    }
    else
    {
        printf("file systems unregister ok\r\n");
    }
}
void fatfs_rw_test()
{
    uint8_t w_buf[] = "this is a test txt\n now time is 2020-9-17 22:13\nend";
    uint8_t r_buf[200] = {0};
    UINT w_buf_len = 0;
    UINT r_buf_len = 0;
    g_res = f_open(&file, "0:2020.txt", FA_OPEN_ALWAYS | FA_WRITE | FA_READ);
    if(g_res != FR_OK)
    {
        printf("open 2020.txt failed err code = %d\r\n",g_res);
    }
    else
    {
        printf("open 2020.txt  ok\r\n");
    }
    g_res = f_write(&file, w_buf, sizeof(w_buf), &w_buf_len);
    if(g_res != FR_OK)
    {
        printf("write 2020.txt failed  err code = %d\r\n",g_res);
    }
    else
    {
        printf("write 2020.txt  ok   w_buf_len = %d\r\n", w_buf_len);
        f_lseek(&file, 0);
        g_res = f_read(&file, r_buf, f_size(&file), &r_buf_len);
        if(g_res != FR_OK)
        {
            printf("read 2020.txt failed g_res = %d\r\n", g_res);
        }
        else
        {
            printf("read 2020.txt  ok   r_buf_len = %d\r\n", r_buf_len);
        }
    }
    f_close(&file);
}
void get_free()
{
    FATFS * fs;
    DWORD fre_clust, fre_sect, tot_sect;

    g_res = f_getfree("0:", &fre_clust, &fs);

    if(g_res == FR_OK)
    {
        tot_sect = (fs-> n_fatent - 2) * fs-> csize;
        fre_sect = fre_clust * fs-> csize;

        /*打印可用空间(假设每扇区512字节)*/
        printf("%lu KB总驱动器空间。\n%lu KB可用\n", tot_sect/2, fre_sect/2);
        printf("csize = %d  fs-> n_fatent = %d fre_clust = %d \n",fs-> csize,fs->n_fatent,fre_clust);
    }
    else 
    {
       printf("get_free failed errcode  = %d\n",g_res);
    }
}
FRESULT scan_files(
    char* path        /* Start node to be scanned (also used as work area) */
)
{
    FRESULT res;
    FILINFO fno;
    DIR dir;
    int i;
    char *fn;   /* This function assumes non-Unicode configuration */
#if _USE_LFN
    static char lfn[_MAX_LFN + 1];   /* Buffer to store the LFN */
    fno.lfname = lfn;
    fno.lfsize = sizeof lfn;
#endif


    res = f_opendir(&dir, path);                       /* Open the directory */
    if(res == FR_OK)
    {
        i = strlen(path);
        for(;;)
        {
            res = f_readdir(&dir, &fno);                   /* Read a directory item */
            if(res != FR_OK || fno.fname[0] == 0) break;   /* Break on error or end of dir */
            if(fno.fname[0] == '.') continue;              /* Ignore dot entry */
#if _USE_LFN
            fn = *fno.lfname ? fno.lfname : fno.fname;
#else
            fn = fno.fname;
#endif
            if(fno.fattrib & AM_DIR)                       /* It is a directory */
            {
                sprintf(&path[i], "/%s", fn);
                res = scan_files(path);
                path[i] = 0;
                if(res != FR_OK) break;
            }
            else                                           /* It is a file. */
            {
                printf("%s/%s\n", path, fn);
            }
        }
        f_closedir(&dir);
    }

    return res;
}

void file_check(void)
{  
    
    static FILINFO finfo;
/* 获取文件信息 */
    g_res=f_stat("0:2020.txt",&finfo);
    if(g_res == FR_OK)
    {
    printf("2020.txt”文件信息:\n");
    printf("》文件大小: %ld(字节)\n", finfo.fsize);
    printf("》时间戳: %u/%02u/%02u, %02u:%02u\n",
           (finfo.fdate >> 9) + 1980, finfo.fdate >> 5 & 15, finfo.fdate & 31,finfo.ftime >> 11, finfo.ftime >> 5 & 63);
    printf("》属性: %c%c%c%c%c\n\n",
           (finfo.fattrib & AM_DIR) ? 'D' : '-',      // 是一个目录
           (finfo.fattrib & AM_RDO) ? 'R' : '-',      // 只读文件
           (finfo.fattrib & AM_HID) ? 'H' : '-',      // 隐藏文件
           (finfo.fattrib & AM_SYS) ? 'S' : '-',      // 系统文件
           (finfo.fattrib & AM_ARC) ? 'A' : '-');     // 档案文件
    }
    else 
    {
        printf("f_stat failed errcode = %d\n",g_res);
    }
}
void set_label()
{
    /* Set volume label to the default drive */
    g_res = f_setlabel("ZhenRobotic");
    if(g_res != FR_OK)
    {
        printf("SET_LABEL errcode = %d\n",g_res);
    }
    else 
    {
        printf("SET_LABEL ok \n");
    }

}
#ifndef _EX_FATFS_H
#define _EX_FATFS_H

#include "stm32f1xx_hal.h"
#include "fatfs.h"
void fatfs_register(void);
void fatfs_unregister(void);

void fatfs_init_test(void);
void fatfs_rw_test(void);
void get_free(void);
void set_label(void);

FRESULT scan_files (char* path);
void file_check(void);

#endif

6、修改main.c文件

int main(void)
{
    /* USER CODE BEGIN 1 */
    char file_name[256];
    /* USER CODE END 1 */

    /* MCU Configuration--------------------------------------------------------*/

    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    HAL_Init();

    /* USER CODE BEGIN Init */

    /* USER CODE END Init */

    /* Configure the system clock */
    SystemClock_Config();

    /* USER CODE BEGIN SysInit */

    /* USER CODE END SysInit */

    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_USART1_UART_Init();
    MX_USB_DEVICE_Init();
    MX_FATFS_Init();
    /* USER CODE BEGIN 2 */
    fatfs_register();
    
    fatfs_rw_test();
    
    strcpy(file_name, "0:");
    scan_files(file_name);
    
    get_free();
    
    file_check();
    
    set_label();
    
    fatfs_unregister();
    /* USER CODE END 2 */

    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while(1)
    {
        /* USER CODE END WHILE */

        /* USER CODE BEGIN 3 */
    }
    /* USER CODE END 3 */
}

7、结果

8、上位机添加文件

9、后记

一开始打算将FLASH_BLOCK_SIZESTORAGE_BLOCK_SIZE设置为1,即2048一个扇区(簇),但是这样的话,在进行格式化的时候会死在上图所示的圈红的地方,这个n_vol是挂载的总扇区大小,如果使用2048一个扇区的话,那么至少要有256K空间。在我的使用场景中是不可能有这么大的,后来经过测试,将FLASH_BLOCK_SIZESTORAGE_BLOCK_SIZE修改为4,在60KB的情况下依然可以使用.

这个时候就已经可以做很多事情了,比如将BIN文件放入U盘内,自动读取BIN文件进行固件升级,比如开辟一段专门的空间去进行log记录(这样的话芯片寿命会大幅度降低)。

其实到了这还是有一些疑问,使用文件系统也好,使用USB操作内部FLASH也好,我分配的是100KB,但是在电脑上看到的,使用文件系统读到的都会比100KB小一部分,这部分空间不知道都在哪。

再比如,同样分配100KB空间,扇区大小设置为512,在使用f_mkfs函数的时候,第二个参数使用0的时候,总空间大小只有50KB,使用1的时候有82KB,这两个参数的区别如下

When FDISK format is specified, a primary partition occupies entire space of the physical drive is created and then an FAT volume is created on the partition. When SFD format is specified, the FAT volume starts from the first sector of the physical drive.

总之,环境搭建到现在已经基本满足需求,结束.

 

 

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页