Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1670056
  • 博文数量: 124
  • 博客积分: 4078
  • 博客等级: 中校
  • 技术积分: 3943
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-21 11:28
个人简介

新博客:http://sparkandshine.net/

文章分类

全部博文(124)

分类: 嵌入式

2011-12-07 16:44:20

摘要:

    本文深入源码讲述了Contiki文件系统Coffee的格式化,即擦除整个FLASH,并初始化结构体protected_mem。


一、格式化

1.1 硬盘格式化

(1)低级格式化

    低级格式化就是将空白的磁盘划分出柱面和磁道,再将磁道划分为若干个扇区,每个扇区又划分出标识部分 ID、间隔区GAP和数据区DATA等。可见,低级格式化是高级格式化之前的一件工作,低级格式化只能针对一块硬盘而不能支持单独的某一个分区。每块硬盘在出厂时,已由硬盘生产商进行低级格式化,因此通常使用者无需再进行低级格式化操作。其实,我们对一张软盘进行的全面格式化就是一种低级格式化。对于硬盘上出现逻辑坏道或者软性物理坏道,用户可以试试使用低级格式化来达到屏蔽坏道的作用,这样能在一定程度上保证用户数据的可靠性,但坏道却会随着硬盘分区、格式化次数的增长而扩散蔓延[3]。

(2)高级格式化

    高级格式化又称逻辑格式化,它是指根据用户选定的文件系统(如FAT12、FAT16、FAT32、NTFS、EXT2、EXT3等),在磁盘的特定区域写入特定数据,以达到初始化磁盘或磁盘分区、清除原磁盘或磁盘分区中所有文件的一个操作。高级格式化包括对主引导记录中分区表相应区域的重写、根据用户选定的文件系统,在分区中划出一片用于存放文件分配表、目录表等用于文件管理的磁盘空间,以便用户使用该分区管理文件[4]。

1.2 Coffee格式化

    跟硬盘格式化类似,FLASH低级格式化(可以如此理解,也许根本就没有,由产家弄好了)已经把FLASH按块-页分好了,Coffee格式化类似于硬盘的高级格式化,但不尽相同。第一使用Coffee文件系统,必须进行格式化,将整个FLASH擦除(FLASH物理特性,先擦除后写)。


二、cfs_coffee_format

    cfs_coffee_format主要工作将Coffee管理的FLASH区域全部写入“0”(而不是擦除,极易被COFFEE_ERASE这个名字误导),以及初始化结构体protected_mem,源码如下:

  1. int cfs_coffee_format(void)
  2. {
  3.   unsigned i;
  4.   PRINTF("Coffee: Formatting %u sectors", COFFEE_SECTOR_COUNT);

  5.   *next_free = 0;
  6.   for (i = 0; i < COFFEE_SECTOR_COUNT; i++)
  7.   {
  8.     COFFEE_ERASE(i);
  9.     PRINTF(".");
  10.   }

  11.   memset(&protected_mem, 0, sizeof(protected_mem)); /* Formatting invalidates the file information.*/

  12.   PRINTF(" done!\n");
  13.   return 0;
  14. }

2.1 next_free

next_free是protected_mem_t结构体的一个成员变量,指向下一个空闲的页,初始化为0,源码如下:

  1. static struct protected_mem_t
  2. {
  3.   struct file coffee_files[COFFEE_MAX_OPEN_FILES];
  4.   struct file_desc coffee_fd_set[COFFEE_FD_SET_SIZE];
  5.   coffee_page_t next_free;
  6.   char gc_wait;
  7. } protected_mem;

  8. static struct file *const coffee_files = protected_mem.coffee_files;
  9. static struct file_desc *const coffee_fd_set = protected_mem.coffee_fd_set;
  10. static coffee_page_t *const next_free = &protected_mem.next_free;
  11. static char *const gc_wait = &protected_mem.gc_wait;

2.2 COFFEE_ERASE

    Coffee格式化是按区擦除,可以把COFFEE_SECTOR_SIZE设成几倍的块大小(片外FLASH,即NAND FLASH)或者几倍的页大小(片上FLASH,即NOR FLASH,按页擦除或整个FLASH擦除),这将有利于容量大的存储设备。[2]是这么说的,COFFEE_SECTOR_SIZE用于应付大的存储设备(比如SD卡),在这种情况下,将其设置大一点可加快顺序扫描速度。

    COFFEE_ERASE宏被定义到硬件相关的擦除函数(这也是Coffee文件移植需做的事,在cfs-coffee-arch.h文件中),如下:

  1. #define COFFEE_ERASE(sector) stm32_flash_erase(sector)

stm32_flash_erase函数源代码(在cfs-coffee-arch.c中)如下:

  1. void stm32_flash_erase(u8_t sector)
  2. {
  3.   u32_t addr = COFFEE_START + (sector) *COFFEE_SECTOR_SIZE;
  4.   u32_t end = addr + COFFEE_SECTOR_SIZE;

  5.   /* This prevents from accidental write to CIB. */
  6.   if (!(addr >= FLASH_START && end <= (FLASH_START+FLASH_PAGES*COFFEE_SECTOR_SIZE)))
  7.   {
  8.     return ;
  9.   }
  10.   FLASH_Unlock();
  11.   FLASH_ErasePage(addr);
  12. }

    咋一看,上述的代码有只有当COFFEE_SECTOR_SIZE(逻辑区大小)与FLASH_PAGE_SIZE(FLASH物理页大小)相等的时候,才不会有问题。若COFFEE_SECTOR_SIZE大于FLASH_PAGE_SIZE,但FLASH_ErasePage只擦除一页,那余下的就没擦除了;倘若COFFEE_SECTOR_SIZE小于FLASH_PAGE_SIZE,那还多擦除了部分。在我们的例子,将COFFEE_SECTOR_SIZE被设成FLASH物理页大小FLASH_PAGE_SIZE,所以没出现问题。

    细心的你会发现,即便如此也还有问题,因为FLASH是页读写,按块擦除,而块通常由若干页组成,而代码却是按页擦除。但你忽略了另一个问题,平台相关性,我们例子的FLASH是片上FLASH,即NOR FLASH,与内存统一编址,可以随机读取,按页擦除或者整块擦除,所以一直没出问题。

    注意:这是我之前的理解,后来发现有误。事实上,COFFEE_ERASE并不是将FLASH擦除,而是将FLASH写入“0”,修改后的源代码如下:

  1. void stm32_flash_erase(u8_t sector)
  2. {
  3.   u32_t data = 0;
  4.   u32_t addr = COFFEE_START + (sector) * COFFEE_SECTOR_SIZE;
  5.   u32_t end = addr + COFFEE_SECTOR_SIZE;
  6.   
  7.   if(!(addr>=COFFEE_START && end<=COFFEE_START+COFFEE_SIZE)) //确保地址在Coffee管理的区间
  8.   {
  9.     return;
  10.   }
  11.   
  12.   FLASH_Unlock();
  13.   FLASH_ErasePage(addr); //先擦除 NOTE:assume COFFEE_SECTOR_SIZE=FLASH_PAGE_SIZE
  14.   for(; addr < end; addr += 4)
  15.   {
  16.     if(FLASH_ProgramWord(addr, data) != FLASH_COMPLETE)
  17.     {
  18.       PRINTF("FLASH_ProgramHalfWord Error.\n");
  19.     }
  20.   }
  21.   FLASH_Lock();
  22. }

2.3 memset

    memset函数功能是将一段内存块填充某个给定的值,通常用于对较大结构体或数组清零操作。memset(void *s, int c, size_t n),即将从地址s开始往后的n个字节都设成c[5]。在这里,将protected_mem结构体(见2.1)的成员变量都设成0(数组的每个元素也为0)。奇怪了,实现这个函数怎么会在Install_Software\IAR Systems\Embedded Workbench 5.4\arm\INC呢?源码如下,完全看不懂:-(

  1. #define _DLIB_STRING_SKIP_INLINE_MEMSET
  2. #pragma inline
  3. void *memset(void *_D, int _C, size_t _N)
  4. {
  5.   __aeabi_memset(_D, _N, _C);
  6.   return _D;
  7. }


至此Coffee文件系统格式化完毕:-)


更多Contiki学习笔记可通过博文《Contiki学习笔记:目录》索引访问。


参考资料:

[1] Enabling Large-Scale Storage in Sensor Networks with the Coffee File System.pdf

[2]

[3] 文章《》

[4] 维基百科词条:

[5] 博文《memset用法详解

阅读(3664) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~