您的位置:首页 > 其它

RT-Thread 学习笔记(七)---开启基于SPI Flash的elmfat文件系统(中)

2015-03-26 21:26 971 查看
软件环境:Win7,Keil MDK 4.72a, IAR EWARM 7.2, GCC 4.2,Python 2.7 ,SCons 2.3.2

硬件环境:Armfly STM32F103ZE-EK v3.0开发板

参考文章:RT-Thread编程指南

[RTthread]新版本RTT中的SPI驱动框架

Github托管的Realtouch分支中examples目录中spi flash的例程

【1】RT-Thread 1.2.x中组件初始化代码分析

上篇文章中介绍了spi flash的相关驱动文件添加和相关代码修改,编译虽然能通过但并不代表能够调试通过。因为针对rt-thread-1.2.x版本采用了组件初始化功能,具体是components.c中实现的,请看下面代码:

/**

* RT-Thread Components Initialization

*/

void rt_components_init(void)

{

#ifndef _MSC_VER

#if RT_DEBUG_INIT

int result;

const struct rt_init_desc *desc;

rt_kprintf("do components intialization.\n");

for (desc = &__rt_init_desc_rti_board_end; desc < &__rt_init_desc_rti_end; desc ++)

{

rt_kprintf("initialize %s", desc->fn_name);

result = desc->fn();

rt_kprintf(":%d done\n", result);

}

#else

const init_fn_t *fn_ptr;

for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++)

{

(*fn_ptr)();

}


#endif

上面代码蓝色粗体是组件初始化的入口,是一个函数指针。init_fn_t 的定义在rtdef.h中,如下所示:

/* initialization export */

#ifdef RT_USING_COMPONENTS_INIT

typedef int (*init_fn_t)(void);

... ...

... ...

#define INIT_EXPORT(fn, level) \

const init_fn_t __rt_init_##fn SECTION(".rti_fn."level) = fn


... ...

... ...

... ...

#endif

其中typdef int (*init_fn_t)(void)的意思是定义init_fn_t为指向函数的指针类型,该函数返回int类型值。这样一来,我们对(*init_fn_t)()的意思就清楚了。

INIT_EXPOT(fn,level) 的表达式是const init_fn_t __rt_init_##fn SECTION(".rti_fn."level) = fn,其中##连词符,SECTION的定义为:

#define SECTION(x) __attribute__((section(x)))

RealView
编译工具 编译器参考指南中给出了下面的解释:

__attribute__((section("name")))


通常,ARM 编译器将它生成的对象放在节中,如
data
bss
。但是,您可能需要使用其他数据节,或者希望变量出现在特殊节中,例如,便于映射到特殊硬件。
section
属性指定变量必须放在特定数据节中。如果使用
section
属性,则将只读变量放在
RO 数据节中,而将读写变量放在 RW 数据节中,除非您使用
zero_init
属性。在这种情况下,变量被放在 ZI 节中。

到此,意思已经很明了了,编译器可以根据对section("name")中的name指定,可以将它生成的数据放到特定的数据节中,下面引用一个网友electrlife的看法:

类似的这样的方式,Linux也提供了一些借鉴,把一个函数的地址(注意是函数地址,而不是函数本身)输出到一个独立的section中,同时按照一定顺序进行排列,例如:

.rti_fn.0

.rti_fn.1

.rti_fn.2

...

.rti_fn.7

这样几个section(这样几个不同的section也给出了排列的顺序)。同时把.rti_fn.0和.rti_fn.7保留给系统使用,分别定义出两个桩放置在这两个点上。也可以按照RT-Thread的形式定义简化的宏:

typedef int (*init_fn_t)(void);

#define INIT_EXPORT(fn, level) \

const init_fn_t __rt_init_##fn SECTION(".rti_fn."level) = fn

#define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1")

#define INIT_CPU_EXPORT(fn) INIT_EXPORT(fn, "2")

#define INIT_DEVICE_EXPORT(fn) INIT_EXPORT(fn, "3")

#define INIT_COMPONENT_EXPORT(fn) INIT_EXPORT(fn, "4")

#define INIT_FS_EXPORT(fn) INIT_EXPORT(fn, "5")

#define INIT_APP_EXPORT(fn) INIT_EXPORT(fn, "6")

INIT_EXPORT宏用于输出一个函数到初始化序列中,相应的可以定义一些更简化的宏。

这样两个桩可以定义成:

static int rti_start(void)

{

return 0;

}

INIT_EXPORT(rti_start, "0");

static int rti_end(void)

{

return 0;

}

INIT_EXPORT(rti_end,"7");

根据这两个桩的位置,简化的rt_components_init()函数就可以变成:

void rt_components_init(void)

{

const init_fn_t* fn_ptr;

for (fn_ptr = &__rt_init_rti_start; fn_ptr < &__rt_init_rti_end; )

{

(*fn_ptr)();

fn_ptr ++;

}

}

事实上,aozima做了工程测试得到了验证:

工程编译后,从map文件找到相关部分内容:

InitFuncSym$$Base 0x00000e18 Number 0init_1.o(InitFuncSym)

__rt_init_init_1 0x00000e18 Data 4init_1.o(InitFuncSym)

__rt_init_init_2 0x00000e1c Data 4init_2.o(InitFuncSym)

__rt_init_init_3 0x00000e20 Data 4init_3.o(InitFuncSym)

__rt_init_init_4 0x00000e24 Data 4init_4.o(InitFuncSym)

__rt_init_init_5 0x00000e28 Data 4init_5.o(InitFuncSym)

__rt_init_init_6 0x00000e2c Data 4init_6.o(InitFuncSym)

InitFuncSym$$Limit 0x00000e30 Number 0init_6.o(InitFuncSym)

尽管数据存放在不同的文件,但从这里这可以看到是空间连续分配的

楼主设定的桩

static int rti_start(void)

{

return 0;

}

INIT_EXPORT(rti_start, "0");

static int rti_end(void)

{

return 0;

}

INIT_EXPORT(rti_end,"7");

可由InitFuncSym$$Base和InitFuncSym$$Limit 代替

最后,初始化过程可以写成

extern int InitFuncSym$$Base;

extern int InitFuncSym$$Limit;

init_fn_t* fn;

for (fn = (init_fn_t *)&InitFuncSym$$Base; fn < (init_fn_t *)&InitFuncSym$$Limit; fn ++ ) {

(*fn)();

}

同样的,ffxz也有下面的测试:

参考RTT下的IAR工程,在ICF文件增加一行

keep { section InitFuncSym };

这行关系到以下信息是否生成.

在Debug/List下的map中有这样的描述

.text ro code0x00000658 0x16xprout.o

InitFuncSym 0x00000670 0x14<Block>

InitFuncSym const 0x00000670 0x4init_1.o

InitFuncSym const 0x00000674 0x4init_2.o

InitFuncSym const 0x00000678 0x4init_4.o

InitFuncSym const 0x0000067c 0x4init_5.o

InitFuncSym const 0x00000680 0x4init_6.o

.rodata const 0x00000684 0x10init_1.o

......

InitFuncSym$$Base 0x00000670 DataGb- Linker created -

InitFuncSym$$Limit 0x00000684 DataGb- Linker created -

总之,通过定义

#define INIT_EXPORT(fn,
level) \

const init_fn_t __rt_init_##fn SECTION(".rti_fn."level) = fn

可以系统各部分的组件通过INIT_EXPORT(fn,level)放到一个特定代码段当中,简言之,当我们要初始化某个组件时,定义完这个初始化函数后,根据上面宏定义的注释,在其下面接着放一条INIT_XXX_EXPORT(fn)就可以了。相当于一个指定到特定代码段的隐形调用,而且要清楚这个段中是不同组件初始化函数的入口地址,例如:

int my_init_fun(void)

{

... ...

}

INIT_XXX_EXPORT(my_init_fun)

根据以上分析,我们需要工程文件做相应的修改:

【2】工程文件修改

(1)打开sdcard.c文件,定位到3241行附近,修改如下:

int rt_hw_sdcard_init(void)

{

/* SDIO POWER */

... ...

__return:

rt_kprintf("sdcard init failed\n");

GPIO_SetBits(GPIOC,GPIO_Pin_6); /* SD card power down */

return 0;

}

//INIT_DEVICE_EXPORT(rt_hw_sdcard_init);

... ...

修改完成后保存,因为还没有sd卡的功能,先注释掉,不让系统调用这个初始化函数。

(2)打开rt_spi_flash_device.c文件,定位到94行附近,修改如下:

... ...

}

INIT_DEVICE_EXPORT(rt_hw_spi_init);

#endif /* RT_USING_SPI */

void rt_spi_flash_device_init(void)

{

#if defined(RT_USING_DFS) && defined(RT_USING_DFS_ELMFAT)

w25qxx_init("flash0", "spi11");

#endif /* RT_USING_DFS && RT_USING_DFS_ELMFAT */

... ...

通过在rt_hw_spi_init()下面加上宏语句“INIT_DEVICE_EXPORT(rt_hw_spi_init);”让系统隐含调用它。当然也就不需要在rt_spi_device_init()函数调用它了。

(3)打开application.c,定位到99行附近,确认已经修改成如下代码:

void rt_init_thread_entry(void* parameter)

{

#ifdef RT_USING_COMPONENTS_INIT

/* initialization RT-Thread Components */

rt_components_init();

#endif

rt_spi_flash_device_init();

#ifdef RT_USING_FINSH

finsh_set_device(RT_CONSOLE_DEVICE_NAME);

#endif /* RT_USING_FINSH */

然后定位到文件开头45行附近,加入外部声明,如下:

#include "led.h"

extern void rt_spi_flash_device_init(void);

ALIGN(RT_ALIGN_SIZE)

static rt_uint8_t led_stack[ 512 ];

static struct rt_thread led_thread;

static void led_thread_entry(void* parameter)

{

unsigned int count=0;

rt_hw_led_init();

... ...

修改完成后保存。

【3】确认Flash底层驱动的芯片型号识别部分代码是否和开发板上硬件对应上

开发板上的SPI Flash芯片型号SST25VF016B,打开数据手册,定位到第19页,可以看到下图所示定义:



制造商ID号0xBF,器件ID是0x4125,打开spi_flash_w25qxx.c,定位到37行附近,修改如下:

/* JEDEC Manufacturer¡¯s ID */

#define MF_ID (0xBF) /*(0xEF)*/

/* JEDEC Device ID: Memory type and Capacity */

#define MTC_W25Q16_BV_CL_CV (0x4015) /* W25Q16BV W25Q16CL W25Q16CV */

#define MTC_W25Q16_DW (0x6015) /* W25Q16DW */

#define MTC_W25Q32_BV (0x4016) /* W25Q32BV */

#define MTC_W25Q32_DW (0x6016) /* W25Q32DW */

#define MTC_W25Q64_BV_CV (0x4017) /* W25Q64BV W25Q64CV */

#define MTC_W25Q64_DW (0x4017) /* W25Q64DW */

#define MTC_W25Q128_BV (0x4018) /* W25Q128BV */

#define MTC_W25Q256_FV (TBD) /* W25Q256FV */

#define MTC_SST25VF016B (0x4125) /* SST25V016B */

然后定位到378行附近,修改如下:

... ...

else if(memory_type_capacity == MTC_W25Q16_DW)

{

FLASH_TRACE("W25Q16DW detection\r\n");

spi_flash_device.geometry.sector_count = 512;

}

else if(memory_type_capacity == MTC_SST25VF016B)

{

FLASH_TRACE("W25Q16DW detection\r\n");

spi_flash_device.geometry.sector_count = 512;

}

else

{

FLASH_TRACE("Memory Capacity error!\r\n");

return -RT_ENOSYS;

}

... ...

修改完成后保存。

(4) 使用Notepad++打开driver目录下的Sconscript,定位到23行附近,把前面添加到driver目录的rt_spi_device.c,rt_stm32f10x_spi.c,spi_flash_w25qxx.c添加到Depend()中,修改如下:

# add DFS drvers.

if GetDepend('RT_USING_DFS'):

src += ['rt_spi_flash_device.c','rt_stm32f10x_spi.c','spi_flash_w25qxx.c']

# add RTC drvers.

if GetDepend('RT_USING_RTC'):

src += ['rtc.c']

这样执行该脚本时,加入驱动文件才能被编译,修改完成后保存。

【4】编译测试

编译后发现如下信息:

drivers\rt_spi_device.c(86): warning: #144-D: a value of type "void (*)(void)" cannot be used to initialize an entity of type "const init_fn_t"

出现告警:原因是rt_hw_spi_init(void)函数出来问题,这个函数应该定义成int 类型而不是void类型,现在改过来,打开rt_spi_device.c定位到21行附近,修改如下:

#ifdef RT_USING_SPI

static int rt_hw_spi_init(void)

{

/* register spi bus */

{

... ...

return 0;

}

INIT_DEVICE_EXPORT(rt_hw_spi_init);

#endif /* RT_USING_SPI */

修改完成后保存,重新编译,OK,编译通过,没有告警。

下载到开发板上运行,结果如下:



Memory 容量错误,挂载失败。

进入跟踪调试模式,打开spi_flash_w25qxx.c,定位到378行,在出现“Memory Capacity error!”地方这个断点,然后点击运行,停到断点处,得到memory_type_capacity的跟踪值如下图所示:



显然是下图中的MTC_SST25VF016B的值定义错了,定位到



往上定位到48行附近,原来是芯片型号定义问题

... ...

#define MTC_W25Q128_BV (0x4018) /* W25Q128BV */

#define MTC_W25Q256_FV (TBD) /* W25Q256FV */

#define MTC_SST25VF016B (0x4125) /* SST25V016B */

... ...

现在修改成和跟踪到memory_type_capacity值相同:

... ...

#define MTC_SST25VF016B (0x2541) /* SST25V016B */

... ...
然后退出调试模式,保存修改,重新编译,下载,运行后,终端信息如下:



可以看到,Flash芯片已经被成功识别,但文件系统还是没有下载成功,因为我们还没有格式化芯片,没有文件系统当然也就识别不到了。下一篇将要研究文件系统相关操作操作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: