|
这个问题源于老的代码:
- 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!\n");
- #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!\n");
- }
- else
- {
- rt_kprintf("Mount FatFs file system failed.\n");
- }
- #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!\n");
- }
- else
- {
- rt_kprintf("Mount RAMDisk failed.\n");
- }
- }
- }
- #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!\n");
- #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 */
- }
复制代码 其中的宏条件、宏定义够多够乱!为了实现不同组件的独立性,不得不加无数的宏条件,“貌似”没什么好办法,但其实这些初始化是很规律性的,或者最简单的一点:编译进代码中的,需要初始化,不编译的自然不需要初始化。
原来有一个components.c,期望它可以进行各个组件的初始化,期望它能够做到:
- #include <components.h>
- 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中没有本质的区别。
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 ++;
- }
- }
复制代码 世界清静了! |
阿莫论坛20周年了!感谢大家的支持与爱护!!
知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)
|