RT-Thread 1.2.0 beta新功能预览 [组件初始化]
这个问题源于老的代码: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();
}
#ifdefRT_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 ++;
}
}
世界清静了! 本帖最后由 electrlife 于 2013-6-22 23:09 编辑
呵呵,这利用编译器段一般按字母排序的特性。uboot等也是这样处理的。
本帖最后由 McuPlayer 于 2013-6-22 23:34 编辑
支持一把RTT
去年一个PRJ打算用,后来因为计划变动项目被无限期Delay了
只做了驱动模块,基于RTT的多任务和事件,用DMA实现低速外设模块的异步读写操作,既不占用CPU时间又可以使得APP简洁 本帖最后由 geniuscode 于 2013-6-22 23:45 编辑
{:lol:} 巧妙 这段代码已经上库了,以后的项目可以用这个初始化方式,不用再写一堆的大段初始化代码了。
后面再加log trace、crc模块,这些也是很有用的模块,特别是log trace 惯性思维,所以用宏理解简单{:sweat:},放开了,就当是跳出井口的青蛙... 下班了,做了个测试工程编译试试
工程编译后,从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)();
}
amwox 发表于 2013-6-24 18:55 static/image/common/back.gif
下班了,做了个测试工程编译试试
工程编译后,从map文件找到相关部分内容:
InitFuncSym$$Base ...
这个只能适用于MDK吧。 增加了个IAR工程
参考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 -
IAR不太熟,模拟仿真一下
没有GCC,请有GCC的同学帮忙测试一下
IAR也支持$$?印象中,IAR支持的很不友好的
为了避免各种编译器的情况,所以在这里是用函数桩的形式,同时也避免如果系统中没定义相应的section,导致这个section是空的情况。 请问ffxz这次文档方面更新的多不多,现在git上的好像比之前的没多多少内容。 1. 这样做,初始化函数是不能带参数的,这个函数libc_system_init(RT_CONSOLE_DEVICE_NAME)是否有问题。
2. 能不能定义一个const的初始化函数指针的数组,如果没有编译的,其函数指针设为空指针,循环执行时不执行,这样是不是也可以,用户的初始化程序可以添加在后面。
还可以把系统任务和系统资源用这种形式来创建,
开发者只需定义需要的资源就好了, 不用去关心如何创建的.
RT_TASK_DEFINE("this task", thisTask, stackSize, prio, ....)
RT_MUTEX_DEFINE("this mutex", thisMutex, .....)
这样各个模块的应用工程师都可以很独立的开发,合并工作就是把代码放到一起编译就OK了,类似于依赖注入一样。 linux内核其实也是这么干的。 pppInit();
modem_system_init();
modem_mg323_init();
modem_mc323_init();
modem_gl868_init();
这些函数的实体定义在哪里呢? tianqing324 发表于 2014-3-7 15:23
pppInit();
modem_system_init();
除了pppInit(),其它都是具体项目的代码。 aozima 发表于 2014-3-8 14:45
除了pppInit(),其它都是具体项目的代码。
没有源码是吧,最近在搞gprs ppp
页:
[1]