[新功能] 组件初始化

发布于 2013-06-22 07:50:15
老的代码:

static void rt_init_thread_entry(void *parameter)
{
#ifdef RT_USING_PM
efm32gg_pm_hw_init();
rt_hw_serial_pm_init();
#endif /* RT_USING_PM */

rt_hw_userled_init();

#ifdef RT_USING_MTD_NAND
rt_hw_mtd_nand_init();
#endif /* RT_USING_MTD_NAND */

#ifdef RT_USING_LOGTRACE
/* initialize log trace component */
log_trace_init();
log_trace_set_device(RT_CONSOLE_DEVICE_NAME);
#ifdef LOG_TRACE_NAND
/* set exception handler */
rt_hw_exception_install(exception_handle);
#endif

log_trace(LOG_TRACE_INFO"log trace initialized!
");
#endif /* RT_USING_LOGTRACE */

/* File System Initialization */
#ifdef RT_USING_DFS
{
/* initialize the device file system */
dfs_init();

#ifdef RT_USING_DFS_ELMFAT
/* initialize the elm chan FatFs file system*/
elm_init();
#endif /* RT_USING_DFS_ELMFAT */

#ifdef RT_USING_NFTL
nftl_attach("nand0");
if (dfs_mount("nand0", "/", "elm", 0, 0) == 0)
{
rt_kprintf("Mount FatFs file system to root, Done!
");
}
else
{
rt_kprintf("Mount FatFs file system failed.
");
}
#endif

#ifdef RT_USING_RAMFS
dfs_ramfs_init();
{
struct dfs_ramfs* ramfs;
ramfs = dfs_ramfs_create((rt_uint8_t*)EFM32_RAMFS_BEGIN, EFM32_RAMFS_SIZE);
if (ramfs != RT_NULL)
{
if (dfs_mount(RT_NULL, "/ramfs", "ram", 0, ramfs) == 0)
{
rt_kprintf("Mount RAMDisk done!
");
}
else
{
rt_kprintf("Mount RAMDisk failed.
");
}
}
}
#endif
}
#endif /* RT_USING_DFS */

/* Initialize I2C bus */
#ifdef RT_USING_I2C
rt_i2c_core_init();
rt_hw_iicbus_init();
#endif /* RT_USING_I2C */

/* Initialize RTC */
#ifdef RT_USING_RTC
#ifdef RT_USING_ALARM
rt_alarm_system_init();
#endif

#ifdef SH3H_USING_RX8025
rt_hw_rx8025_init(EFM32GG_IICBUS1_NAME);
#endif

#ifdef EFM32GG_USING_CHIP_RTC
rt_hw_rtc_init();
#endif

#ifdef SH3H_USING_RX8564
rt_hw_rx8564_init(EFM32GG_IICBUS0_NAME);
#endif
#endif /* RT_USING_RTC */

/* Initialize Watchdog */
#ifdef RT_USING_WDT
#ifdef EFM32GG_USING_CHIP_WDT
rt_hw_wdt_init();
#endif

#ifdef SH3H_USING_X4043_WDT
rt_hw_x4043_init(EFM32GG_IICBUS1_NAME);
#endif

#ifdef SH3H_USING_STM6822_WDT
rt_hw_stm6822_init();
#endif

wdtmgr_init(SH3H_STM6822_WDT_NAME);
#endif /* RT_USING_WDT */

#ifdef RT_USING_LWIP
/* initialize lwip system */
eth_system_device_init();
lwip_system_init();
rt_kprintf("TCP/IP initialized!
");

#ifdef RT_LWIP_PPP
/* initialize ppp protocol */
pppInit();

modem_system_init();
modem_mg323_init();
modem_mc323_init();
modem_gl868_init();
#endif /* RT_LWIP_PPP */

#endif /* RT_USING_LWIP */

#ifdef RT_USING_USB_DEVICE
rt_hw_usbd_init();

rt_usb_device_init("usbd");

#if defined(RT_USING_LWIP) && defined(RT_USB_DEVICE_RNDIS)
{
extern void dhcpd_start(void);
extern void ssdp_start(void);
extern void telnet_srv(void);

dhcpd_start();
ssdp_start();
telnet_srv();
}

#ifdef RT_USING_WEBNET
{
extern void httpd_init(void);

httpd_init();
}
#endif /* RT_USING_WEBNET */
#endif // RT_USING_LWIP && RT_USB_DEVICE_RNDIS

#endif /* RT_USING_USB_DEVICE */

#ifdef RT_USING_PM
{
rt_pm_release(PM_RUNNING_MODE);
rt_pm_release(PM_SLEEP_MODE);
rt_pm_request(PM_TIMER_MODE);

rt_hw_plug_init();
}
#endif /* RT_USING_PM */

/* init finsh */
#ifdef RT_USING_FINSH
finsh_system_init();
finsh_set_device(RT_CONSOLE_DEVICE_NAME);
#endif /* RT_USING_FINSH */
}


其中的宏条件、宏定义够多够乱!为了实现不同组件的独立性,不得不加无数的宏条件,“貌似”没什么好办法,但其实这些初始化是很规律性的,或者最简单的一点:编译进代码中的,需要初始化,不编译的自然不需要初始化。

查看更多

关注者
0
被浏览
31k
39 个回答
bernard
bernard 2013-06-22
原来有一个components.c,期望它可以进行各个组件的初始化,期望它能够做到:

#include

static void rt_init_thread_entry(void *parameter)
{
rt_components_init();
}


然后用户的代码自然也就清晰了,再乱也只是乱在rt_components_init()的实现里:

/**
* RT-Thread Components Initialization
*/
void rt_components_init(void)
{
#ifdef RT_USING_MODULE
rt_system_module_init();
#endif

#ifdef RT_USING_FINSH
/* initialize finsh */
finsh_system_init();
finsh_set_device(RT_CONSOLE_DEVICE_NAME);
#endif

#ifdef RT_USING_LWIP
/* initialize lwip stack */
/* register ethernetif device */
eth_system_device_init();

/* initialize lwip system */
lwip_system_init();
#endif

#ifdef RT_USING_DFS
/* initialize the device file system */
dfs_init();

#ifdef RT_USING_DFS_ELMFAT
/* initialize the elm chan FatFS file system*/
elm_init();
#endif

#if defined(RT_USING_DFS_NFS) && defined(RT_USING_LWIP)
/* initialize NFSv3 client file system */
nfs_init();
#endif

#ifdef RT_USING_DFS_YAFFS2
dfs_yaffs2_init();
#endif

#ifdef RT_USING_DFS_UFFS
dfs_uffs_init();
#endif

#ifdef RT_USING_DFS_JFFS2
dfs_jffs2_init();
#endif

#ifdef RT_USING_DFS_ROMFS
dfs_romfs_init();
#endif

#ifdef RT_USING_DFS_DEVFS
devfs_init();
#endif
#endif /* end of RT_USING_DFS */

#ifdef RT_USING_NEWLIB
libc_system_init(RT_CONSOLE_DEVICE_NAME);
#else
/* the pthread system initialization will be initiallized in libc */
#ifdef RT_USING_PTHREADS
pthread_system_init();
#endif
#endif

#ifdef RT_USING_RTGUI
rtgui_system_server_init();
#endif

#ifdef RT_USING_USB_HOST
rt_usb_host_init();
#endif

return;
}


一样的宏条件,一样的糟糕的代码。特别是,一个巨大的问题:components.c属于rt-thread里的代码,如果每个用户都需要自己再去修改里面的代码,那么和把初始化代码放在application.c中没有本质的区别。
bernard
bernard 2013-06-22
components.c的缺陷在于,依然不能摆脱宏条件的实现方式,另一个,用户不能够很方便的加入到初始化的序列中。

不过finsh shell中灵活地把一个函数输出到命令行中,为我们提供一丝灵感。既然能够让用户的程序输出到命令行中,那么也自然能够让用户初始化函数输出到 组件初始化 序列中。

finsh shell的实现方式是把函数入口放到了一个独立的section中,然后再通过编译+链接过程中,获取这个section首地址、尾地址的形式,从而实现系统中所有输出符号的序列。同时为了能够在shell中按照函数名称来查找,在这个独立的section中实际填写的是:
/* system call table */
struct finsh_syscall
{
const char* name; /* the name of system call */
#if defined(FINSH_USING_DESCRIPTION) && defined(FINSH_USING_SYMTAB)
const char* desc; /* description of system call */
#endif
syscall_func func; /* the function address of system call */
};

这样一个结构体。记得论坛上好像有人提及到FINSH_USING_DESCRIPTION和FINSH_USING_SYMTAB的意义来着,其实就是决定shell中列出的命令函数列表,是否有帮助、描述信息。

name给出了函数的名称(所以也能够使用FINSH_FUNCTION_EXPORT_ALIAS宏把一个函数名更改成一个别名),shell中就是使用这个名称来查找对应的func函数指针。section FSymTab存放的则是这一个个struct finsh_syscall结构体。

好像有些扯远了,回过头来说组件初始化。

类似的这样的方式,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 ++;
}
}


世界清静了!
xiao苦
xiao苦 2013-06-22
这个方式确实很不错呢, 不过如果关掉finsh,这个还能这么用吗???
bernard
bernard 2013-06-22
这个功能和finsh无关,独立的,只需要把组件管理打开,这个就打开了。今天晚上完成代码,然后推到github上去
geniusgogo
geniusgogo 2013-06-23
.rti_fn.0
.rti_fn.1
.rti_fn.2
...
.rti_fn.7

如何保证这8个section是按顺序排列的? [s:179]
shaolin
shaolin 2013-06-23
.rti_fn.0
.rti_fn.1
.rti_fn.2
...
.rti_fn.7

如何保证这8个section是按顺序排列的? [s:179]


这个就靠编译器了。
bernard
bernard 2013-06-23
这个顺序,GNU GCC、Keil MDK是没有问题的。其它还有待验证,我的机器装不了IAR啊
geniusgogo
geniusgogo 2013-06-23
finsh_system_init();
finsh_set_device(RT_CONSOLE_DEVICE_NAME);
像这种有初始化顺序的组合步骤,在符号导出之后不好控制他们被调用的次序吧? [s:194]
aozima
aozima 2013-06-23
测试了一下,MDK还要需要像finsh一样 “--keep __rt_init*”
GCC和IAR中还不行,链接时被扔掉了。
weety
weety 2013-06-23
顶,这个改进太棒了!
bernard
bernard 2013-06-23
Keil MDK已加。GCC需要更改ld script,具体见lpc176x分支。
grissiom
grissiom 2013-06-24
MDK 也要修改链接脚本吧……
bernard
bernard 2013-06-24
MDK 也要修改链接脚本吧……


不需要,反正Keil MDK是工程文件化的东西。
aozima
aozima 2013-06-24
发现链接后顺序有不对:
 VSymTab        0x08012ae8       0x10 buildcomponentsfinshcmd.o
0x08012ae8 __vsym_dummy
0x08012af8 __vsymtab_end = .
0x08012af8 . = ALIGN (0x4)
0x08012af8 __rt_init_start = .
*(.rti_fn*)
.rti_fn.3 0x08012af8 0x4 buildcomponentsfinshshell.o
0x08012af8 __rt_init_finsh_system_init
.rti_fn.0 0x08012afc 0x4 buildcomponentsinitcomponents.o
0x08012afc __rt_init_rti_start
.rti_fn.1.post
0x08012b00 0x4 buildcomponentsinitcomponents.o
0x08012b00 __rt_init_rti_board_end
.rti_fn.7 0x08012b04 0x4 buildcomponentsinitcomponents.o
0x08012b04 __rt_init_rti_end
0x08012b08 __rt_init_end = .
0x08012b08 . = ALIGN (0x4)
0x08012b08 . = ALIGN (0x4)
0x08012b08 _etext = .
0x08012b08 __exidx_start = .


链接脚本:
        . = ALIGN(4);
__rt_init_start = .;
KEEP(*(.rti_fn*))
__rt_init_end = .;
. = ALIGN(4);
aozima
aozima 2013-06-24
在链接脚本中指明需要按名称排序:
KEEP(*(SORT(.rti_fn*)))


现在看来是正确的排序了,但总感觉这事不太靠谱。
 VSymTab        0x08012ae8       0x10 buildcomponentsfinshcmd.o
0x08012ae8 __vsym_dummy
0x08012af8 __vsymtab_end = .
0x08012af8 . = ALIGN (0x4)
0x08012af8 __rt_init_start = .
*(SORT(.rti_fn*))
.rti_fn.0 0x08012af8 0x4 buildcomponentsinitcomponents.o
0x08012af8 __rt_init_rti_start
.rti_fn.1.post
0x08012afc 0x4 buildcomponentsinitcomponents.o
0x08012afc __rt_init_rti_board_end
.rti_fn.3 0x08012b00 0x4 buildcomponentsfinshshell.o
0x08012b00 __rt_init_finsh_system_init
.rti_fn.7 0x08012b04 0x4 buildcomponentsinitcomponents.o
0x08012b04 __rt_init_rti_end
0x08012b08 __rt_init_end = .
0x08012b08 . = ALIGN (0x4)
0x08012b08 . = ALIGN (0x4)
0x08012b08 _etext = .
0x08012b08 __exidx_start = .
weety
weety 2013-06-25
这里采用的方法只适用于不带参数的组件初始化函数,带参数的还是不合适,是否需要再改进,现在组件初始化带参数的问题还是标准不统一造成的,例如部分组件需要带设备名。代码:
#ifdef RT_USING_USB_DEVICE
/* usb device controller driver initilize */
rt_hw_usbd_init();

rt_usb_device_init("usbd");
#endif /* RT_USING_USB_DEVICE */


这里的rt_usb_device_init似乎就不能使用组件初始化的方法了,或许这种就不需要采用组件初始化方式。
grissiom
grissiom 2013-06-25
233,C 要是支持 closure 就好了……
bernard
bernard 2013-06-25
这里采用的方法只适用于不带参数的组件初始化函数,带参数的还是不合适,是否需要再改进,现在组件初始化带参数的问题还是标准不统一造成的,例如部分组件需要带设备名。代码:
#ifdef RT_USING_USB_DEVICE
/* usb device controller driver initilize */
rt_hw_usbd_init();

rt_usb_device_init("usbd");
#endif /* RT_USING_USB_DEVICE */


这里的rt_usb_device_init似乎就不能使用组件初始化的方法了,或许这种就不需要采用组件初始化方式。


是的,这部分以后需要规范初始化的接口了,而不是以传递参数的形式。或者由BSP、board中,编写额外的函数来初始化。
shaolin
shaolin 2013-06-26
这里采用的方法只适用于不带参数的组件初始化函数,带参数的还是不合适,是否需要再改进,现在组件初始化带参数的问题还是标准不统一造成的,例如部分组件需要带设备名。代码:
#ifdef RT_USING_USB_DEVICE
/* usb device controller driver initilize */
rt_hw_usbd_init();

rt_usb_device_init("usbd");
#endif /* RT_USING_USB_DEVICE */


这里的rt_usb_device_init似乎就不能使用组件初始化的方法了,或许这种就不需要采用组件初始化方式。


如果采用组件初始化的方式,那么 rt_usb_device_init("usbd"); 这也相应的修改过来,重新考虑下初始化方式。
ufbycd
ufbycd 2013-07-02

如果采用组件初始化的方式,那么 rt_usb_device_init("usbd"); 这也相应的修改过来,重新考虑下初始化方式。


可以借鉴 linux 引导内核时, 用struct tag存储参数,再传递给内核来启动。
bernard
bernard 2013-07-02

如果采用组件初始化的方式,那么 rt_usb_device_init("usbd"); 这也相应的修改过来,重新考虑下初始化方式。


可以借鉴 linux 引导内核时, 用struct tag存储参数,再传递给内核来启动。


这样会比较麻烦,总是得弄些启动参数什么的,难道还要加一个boot loader?
ufbycd
ufbycd 2013-07-02

这样会比较麻烦,总是得弄些启动参数什么的,难道还要加一个boot loader?

我的意思是借鉴,具体实施可以用别的方式,如,加一个存储初始化函数及其参数的链表。
个人意见,仅供参考。
另,目前的初始化方式确实有点麻烦,确实希望改善。
aozima
aozima 2013-07-02
rt_err_t rt_usb_device_init(const char* udc_name)

这类代码后面肯定要改进的,不然多个控制器的情况下没法用。
grissiom
grissiom 2013-07-02

这样会比较麻烦,总是得弄些启动参数什么的,难道还要加一个boot loader?

我的意思是借鉴,具体实施可以用别的方式,如,加一个存储初始化函数及其参数的链表。
个人意见,仅供参考。
另,目前的初始化方式确实有点麻烦,确实希望改善。


参数这个东西,运行时发生改变才有意义,要不真不如直接在 rtconfig.h 或其他配置头文件里配置了完事。如果要是用参数,还要一个参数的 parser,费力不讨好。

Linux 搞这个是因为情况太复杂,有时候需要一次编译,多种模式运行、调试。这个在咱们这里暂时不需要。

P.S. 新初始化机制在 simulator 里不起作用,包括 RTGUI 在内的东西都 break 掉了…… 因为机制的不同(VC 里对段的设定很脑残……),这块貌似不是特别好整…… 我试试看~
chenytscut
chenytscut 2013-07-08
使用了新的组件初始化模式,但是用KEIL编译后,却无法调用到组件定义的程序;例如INIT_COMPONENT_EXPORT(finsh_system_init);
KEIl(V4.7)编译demo程序STM107后:
__rt_init_rti_start			0x0801a2e0   Data	4  components.o(.rti_fn.0)
__rt_init_rti_board_end 0x0801a2e4 Data 4 components.o(.rti_fn.1.post)
__rt_init_rti_end 0x0801a2e8 Data 4 components.o(.rti_fn.7)

找不到finsh_system_init。导致程序无法运行到finsh_system_init。

请问以上问题是KEIL哪里需要另外配置吗,望指教,谢谢!
aozima
aozima 2013-07-08
找不到finsh_system_init。导致程序无法运行到finsh_system_init。

请问以上问题是KEIL哪里需要另外配置吗,望指教,谢谢!

需要在linker选项中添加“--keep __rt_init*” , 跟finsh类似,使用scons生成工程会自动添加。
chenytscut
chenytscut 2013-07-09
1.2.0beta版本调试finsh时发现finsh_set_device没有初始化;
1.1.0组件未初始化时finsh初始化为
#ifdef RT_USING_FINSH
/* init finsh */
finsh_system_init();
finsh_set_device(RT_CONSOLE_DEVICE_NAME);
#endif

1.2.0beta “finsh_system_init”在组件中被调用,但finsh_set_device未调用。
建议放置
int finsh_system_init(void)
{
rt_err_t result;
...
if (result == RT_EOK)
rt_thread_startup(&finsh_thread);

finsh_set_device(RT_CONSOLE_DEVICE_NAME);

return 0;
}

调试OK
bernard
bernard 2013-07-09
是的,这部分需要重新考虑的。如果是这样使用,那么RT_CONSOLE_DEVICE_NAME就必须显时的定义出来
geniusgogo
geniusgogo 2013-08-06
在链接脚本中指明需要按名称排序:
KEEP(*(SORT(.rti_fn*)))


现在看来是正确的排序了,但总感觉这事不太靠谱。
 VSymTab        0x08012ae8       0x10 buildcomponentsfinshcmd.o
0x08012ae8 __vsym_dummy
0x08012af8 __vsymtab_end = .
0x08012af8 . = ALIGN (0x4)
0x08012af8 __rt_init_start = .
*(SORT(.rti_fn*))
.rti_fn.0 0x08012af8 0x4 buildcomponentsinitcomponents.o
0x08012af8 __rt_init_rti_start
.rti_fn.1.post
0x08012afc 0x4 buildcomponentsinitcomponents.o
0x08012afc __rt_init_rti_board_end
.rti_fn.3 0x08012b00 0x4 buildcomponentsfinshshell.o
0x08012b00 __rt_init_finsh_system_init
.rti_fn.7 0x08012b04 0x4 buildcomponentsinitcomponents.o
0x08012b04 __rt_init_rti_end
0x08012b08 __rt_init_end = .
0x08012b08 . = ALIGN (0x4)
0x08012b08 . = ALIGN (0x4)
0x08012b08 _etext = .
0x08012b08 __exidx_start = .

如上所示,在结合如下代码分析:
void rt_components_board_init(void)
{
#ifndef _MSC_VER
#if RT_DEBUG_INIT
int result;
const struct rt_init_desc *desc;
for (desc = &__rt_init_desc_rti_start; desc < &__rt_init_desc_rti_board_end; desc ++)
{
rt_kprintf("initialize %s", desc->fn_name);
result = desc->fn();
rt_kprintf(":%d done
", result);
}
#else
const init_fn_t *fn_ptr;

for (fn_ptr = &__rt_init_rti_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)
{
(*fn_ptr)();
}
#endif
#endif
}

发现组件初始化只从__rt_init_rti_start开始到__rt_init_rti_board_end就结束了,那从__rt_init_rti_board_end到__rt_init_end之间的在哪儿初始化?
geniusgogo
geniusgogo 2013-08-06
看漏了,rt_components_init函数中还有一段。 [s:154]
vv1133
vv1133 2014-02-02
linux中也是这么做的
jouson
jouson 2014-03-11
找了老半天,之前一个tcpip的线程初始化了两次。原来这里是第一次初始化。。
泰瑞宝
泰瑞宝 2014-05-04
请问楼主,代码了有USB-HOST初始化的代码,请问是基于F103的吗,是否可以应用到F107上?
flyxin
flyxin 2014-05-08
可惜看了大半天,弄不明白,看这个需要什么基础嘛,恶补一下 [s:170]
aozima
aozima 2015-01-12
今天在bootloader中使用这个功能,结果发现有些东西没初始化,或初始化时直接hardfault了,调试并对比map文件发现*rti_fn*段的内容分散在多片ROM空间去了。
因为bootloader中挖空了一块小扇区FLASH用来存储参数,所以链接时有些内容被分散到不同的ROM区去了。
所以在链接脚本中指定一下把所有*rti_fn*段放在这个区就可以了。

MDK的SCT文件片段参考
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

; load region size_region
LR_IROM1 (0x08000000) (1024 * 32)
{
; load address = execution address
ER_IROM1 0x08000000 (1024 * 32)
{
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}

; RW data
RW_IRAM1 0x20000000 0x00020000
{
.ANY (+RW +ZI)
}
}

; load region size_region
LR_IROM2 (0x08010000) (1024 * 64)
{
; load address = execution address
ER_IROM2 (0x08010000) (1024 * 64)
{
.ANY (+RO)
*(.rti_fn.*)
}
}
neal
neal 2018-03-19
这个功能MDK支持吗,我编译出来的.map文件里面没有这些符号啊。得是只能用gcc编译

撰写答案

请登录后再发布答案,点击登录

发布
问题

分享
好友