请选择 进入手机版 | 继续访问电脑版

芯虎论坛

 找回密码
 立即注册
查看: 4492|回复: 7

使用HC32F460内部FLASH制作成U盘

[复制链接]

22

主题

34

帖子

430

积分

虎背熊腰

Rank: 3Rank: 3

积分
430
发表于 2021-3-11 11:58:59 | 显示全部楼层 |阅读模式
本帖最后由 yowen_2007 于 2021-3-11 12:15 编辑

在HC32F460 USB msc功能下使用内部Flash制作成小U盘。

HC32F460 内部USB FS支持Device和OTG,支持msc协议。

使用华大官方例程hc32f46x_ddl_Rev1.2.0\example\usb\usbd_msc,此例程使用外部Flash作为U盘空间。
想把存储器作为U盘FAT32文件系统挂载,需要在此例程中实现以下几个函数:
int8_t STORAGE_Init(uint8_t lun);
int8_t STORAGE_GetCapacity(uint8_t lun, uint32_t *block_num, uint32_t *block_size);
int8_t STORAGE_IsReady(uint8_t lun);
int8_t STORAGE_IsWriteProtected(uint8_t lun);
int8_t STORAGE_Read(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);
int8_t STORAGE_Write(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);
int8_t STORAGE_GetMaxLun(void);


其中主要的是GetCapacity,Read,Write。
1. GetCapacity 获取U盘block大小和block个数,上位机可以通过这连个信息计算出U盘容量。此处需要注意FAT32格式需要用到大概20K的空间来存储文件系统信息。
    所以我想用内部Flash 32k空间作为小U盘使用,有20K被文件系统信息占用。剩下存储空间大概剩下12KB。block个数是64个,一个block大小是512字节。
2.Read 读U盘文件系统的的数据,buf是读取数据存储的内存指针,blk_addr是读取数据在整个文件系统内的偏移(从0开始计数的,第几个block)
   blk_len 本次读取数据的长度,同样也是指block个数。FAT32 一个block设定为512字节。
3.Write 函数的blk_addr,blk_len ,和Read函数中的意义一样。

FAT32格式:



对于HC32F460 需要考虑到,read flash内容因为有ARM AHB加持,是可以做到字节读,所以只需要类似memcpy函数直接复制给buf。
另外,HC32F460内部flash只能分页擦除,写可以调用SDK EFM API单字节写入(EFM_SingleProgram)。
而Flash的特点就是想写入必须要擦除,再写入。这样就需要一个8K大小的RAM作为写入时一个page的缓存。
因为在FAT32协议下,一次写入一个block,而不改动其他page中的block内容。这样就需要我们先把一个page读出到RAM,再在此缓存RAM内修改对应block内容后,整页page再写入到对应FLash page中。
比如我想修改Flash page中第二个block中的内容,其他block数据不变。就需要先把page读到RAM,再把要写入的数据写入RAM中第二个block的位置,其他block不动,然后整个8k RAM数据整体再写入到对应FLash page中。

所以通过以上分析,实际上我们要实现的主要三个函数GetCapacity 、Read 、Write ,最主要的就是Write 这个函数的实现。实现需要考虑以下几点:

1. Write需要保证FAT32写入一个512字节block而不改变HC32F460内部flash 一个page中的其他block数据。
2. 定位Write传入的blk_addr在哪一个内部flash page上,而这个blk_addr对于这个page的偏移是多少。
3. 如果Write传入的blk_len 长度跨越了一个page,或已经跨越了多个page,那么我们需要考虑跨page写。

以下就是 这个Write的实现,做到了在32k flash内对任意512字节的block,任意位置,任意长度的写入。

#define INNER_FLASH_START   0x00076000   // 内部FLash作为U盘使用时的开始地址
#define INNER_FLASH_SIZE    0x8000              //  Flash 大小
#define INNER_FLASH_PAGE_SIZE    (1024u * 8u)   //  内部Flash一个page大小
#define INNER_FLASH_BLOCK_SIZE    512u               //  Fat32协议中一个读写单位(block)的大小



// 以下内容中 ,temp_flash是8K大小的RAM空间,作为写入时先读取一个Flash page的缓存。
//



int8_t STORAGE_Write(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
    int8_t res = (int8_t)0;


    uint32_t i = 0;
    uint32_t pg_sum = 0;     // 写数据一共落到了几个page上
    uint32_t blk_offset = 0; // 一个page内,blk_addr落在了第几块block处
    uint32_t pg_num = 0;     // 从第几个page开始
    uint32_t data_sz = 0;    // 写入数据大小(多少块block)
    uint32_t blk_left = 0;   // 还剩下多少块block数据没写
    /* blk_max_num U盘中一共多少块block */
    uint32_t blk_max_num =
        INNER_FLASH_SIZE / INNER_FLASH_BLOCK_SIZE;
    /* blk_mun_ppg 每page页内有多少块block */
    uint32_t blk_mun_ppg =
        INNER_FLASH_PAGE_SIZE / INNER_FLASH_BLOCK_SIZE;


    if ((!buf) || (blk_addr >= blk_max_num)
     || (!blk_len) || (blk_len > blk_max_num))
    {
        printf("[%s] param err\r\n", __FUNCTION__);
        return -1;
    }
    blk_left = blk_len;
    blk_offset = blk_addr % blk_mun_ppg;
    pg_num = blk_addr / blk_mun_ppg;


    if (blk_len % blk_mun_ppg)
        pg_sum = (blk_len / blk_mun_ppg) + 1;
    else
        pg_sum = (blk_len / blk_mun_ppg);


    /* (blk_mun_ppg - blk_offset):是一个page内还剩下多少个block可写 */
    if (blk_left > (blk_mun_ppg - blk_offset)) {
        data_sz = blk_mun_ppg - blk_offset;
    }
    else {
        data_sz = blk_left;
    }


    for (i = 0; i < pg_sum; i++)
    {
        if (inner_flash_Write(pg_num, blk_offset, data_sz * INNER_FLASH_BLOCK_SIZE,
             &buf[(blk_len - blk_left) * INNER_FLASH_BLOCK_SIZE] ))
        {
            printf("[%s] inner_flash_Write1 err\r\n", __FUNCTION__);
            return res;
        }


        pg_num++;
        blk_offset = 0;
        blk_left = blk_left - data_sz;


        if (blk_left > blk_mun_ppg)
            data_sz = blk_mun_ppg;
        else
            data_sz = blk_left;
    }


    return res;
}


同样还需要实现GetCapacity和Read。以下是实现:


实现后把板子插入电脑看效果:
  格式化U盘,可以看到U盘容量大小,格式,分配单元大小。

U盘存入文件:



帖子篇幅限制,需要inner_flash_Write这个函数的可以联系我,给我留言。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

22

主题

34

帖子

430

积分

虎背熊腰

Rank: 3Rank: 3

积分
430
 楼主| 发表于 2021-3-11 12:05:53 | 显示全部楼层
还有一点注意,HC32F460 内部最后一个page的flash中,最后32字节是不可以写入的。
一执行写入操作就会 HardFault,所以不要用最后一个page实现U盘的存储空间。
回复

使用道具 举报

0

主题

3

帖子

12

积分

虎视眈眈

Rank: 1

积分
12
发表于 2021-9-23 17:51:50 | 显示全部楼层
请发给我这个inner_flash_Write函数,谢谢楼主!
回复

使用道具 举报

0

主题

5

帖子

30

积分

虎视眈眈

Rank: 1

积分
30
发表于 2022-5-18 19:18:48 | 显示全部楼层
最近也在做这个,想学习一下楼主的inner_flash_Write函数,谢谢楼主!
回复

使用道具 举报

0

主题

5

帖子

30

积分

虎视眈眈

Rank: 1

积分
30
发表于 2022-5-19 15:42:29 | 显示全部楼层
本帖最后由 eyckacasa 于 2022-5-23 09:42 编辑

这是我写的inner_flash_Write(),在实际运行中,可以格式化,能够创建文件,但是保存不了文件的数据,一直让我保存,如果楼主看到,请指教一下!谢谢!

int8_t inner_flash_Write(uint32_t pg_num, uint32_t blk_offset, uint32_t NumByteToWrite,uint8_t *Buffer)
{
  char FlashStatus = 0;
        int i=0;
  uint32_t writedata = 0x00000000;
               
        uint32_t WriteAddr;
        WriteAddr=INNER_FLASH_START+(INNER_FLASH_PAGE_SIZE*pg_num)+ (blk_offset * INNER_FLASH_BLOCK_SIZE);
        int8_t res = (int8_t)0;
        
        EFM_Unlock();
        EFM_FlashCmd(Enable);
        EFM_SectorErase(INNER_FLASH_START+(INNER_FLASH_PAGE_SIZE*pg_num));

        while((Set != EFM_GetFlagStatus(EFM_FLAG_RDY)) && i<5000)
        {
                //hd_usb_mdelay(1);
                i++;
        }        
        i=0;
        while(i<NumByteToWrite)
                {
                                writedata |= Buffer[i+3]<<24;
                                writedata |= Buffer[i+2]<<16;
                                writedata |= Buffer[i+1]<<8;
                                writedata |= Buffer;        

                                FlashStatus = EFM_SingleProgram(WriteAddr,writedata); //写入数据
                                writedata = 0x00000000;
                                if(FlashStatus != 0)break;
                                WriteAddr = WriteAddr + 4;
                          i +=4;        
                }
        return res;
}


回复

使用道具 举报

0

主题

5

帖子

30

积分

虎视眈眈

Rank: 1

积分
30
发表于 2022-5-23 14:46:04 | 显示全部楼层
本帖最后由 eyckacasa 于 2022-5-23 14:47 编辑
eyckacasa 发表于 2022-5-19 15:42
这是我写的inner_flash_Write(),在实际运行中,可以格式化,能够创建文件,但是保存不了文件的数据,一直让 ...

我目前可以格式化,可以创建文件,但是是0字节可用。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

0

主题

5

帖子

30

积分

虎视眈眈

Rank: 1

积分
30
发表于 2022-5-24 11:43:06 | 显示全部楼层
eyckacasa 发表于 2022-5-23 14:46
我目前可以格式化,可以创建文件,但是是0字节可用。

我是用的是华大最新的源码,选择的也usb_dev_msc这个工程,编译会有些报错,按照信息包含文件就可以得到一个0报错的初始文件。
我是直接针对 其中msc_getcapacity msc_read msc_write 三个函数按照楼主的方法进行修改,之前我发的inner_flash_Write函数错误主要是没有把1页8K的内容拷贝到RAM中就直接修改了,说白了还是没有梳理一下楼主说的几个要点。
回复

使用道具 举报

0

主题

5

帖子

30

积分

虎视眈眈

Rank: 1

积分
30
发表于 2022-5-24 11:44:26 | 显示全部楼层
下面是我写的函数:
int8_t inner_flash_Write(uint32_t pg_num, uint32_t blk_offset, uint32_t NumByteToWrite,uint8_t *Buffer)
{
  char FlashStatus = 0;
        uint32_t i=0;
        uint32_t j=0;
  uint32_t writedata = 0x00000000;
               
        uint32_t WriteAddr;
        WriteAddr=INNER_FLASH_START+(INNER_FLASH_PAGE_SIZE*pg_num);
        int8_t res = (int8_t)0;
       
        memcpy(buffer1,(int8_t *)(INNER_FLASH_START + pg_num * INNER_FLASH_PAGE_SIZE ),16 * INNER_FLASH_BLOCK_SIZE);
         
        for (j = 0; j < NumByteToWrite; j++)
        {
                buffer1[(blk_offset * 512) + j] = Buffer[j];
        }
       
       
        EFM_Unlock();
        EFM_FlashCmd(Enable);
        EFM_SectorErase(INNER_FLASH_START+(INNER_FLASH_PAGE_SIZE*pg_num));

        while((Set != EFM_GetFlagStatus(EFM_FLAG_RDY)) && i<5000)
        {
                //hd_usb_mdelay(1);
                i++;
        }       
        i=0;
        while(i<8192)
                {
                                writedata |= buffer1[i+3]<<24;
                                writedata |= buffer1[i+2]<<16;
                                writedata |= buffer1[i+1]<<8;
                                writedata |= buffer1[i];       

                                FlashStatus = EFM_SingleProgram(WriteAddr,writedata); //写入数据
                          writedata = 0x00000000;
                                if(FlashStatus != 0)break;
                                WriteAddr = WriteAddr + 4;
                          i +=4;       
                }
        return res;
}
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|芯虎论坛 ( 辽ICP备18019618号 )

GMT+8, 2022-7-7 02:40 , Processed in 0.183160 second(s), 19 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表