搜索
bottom↓
回复: 16

RT-Thread 1.2.0 beta新功能预览 [组件初始化]

[复制链接]

出0入0汤圆

发表于 2013-6-22 22:47:52 | 显示全部楼层 |阅读模式
这个问题源于老的代码:

  1. static void rt_init_thread_entry(void *parameter)
  2. {
  3. #ifdef RT_USING_PM
  4.     efm32gg_pm_hw_init();
  5.     rt_hw_serial_pm_init();
  6. #endif /* RT_USING_PM */

  7.     rt_hw_userled_init();

  8. #ifdef RT_USING_MTD_NAND
  9.     rt_hw_mtd_nand_init();
  10. #endif /* RT_USING_MTD_NAND */

  11. #ifdef RT_USING_LOGTRACE
  12.     /* initialize log trace component */
  13.     log_trace_init();
  14.     log_trace_set_device(RT_CONSOLE_DEVICE_NAME);
  15. #ifdef LOG_TRACE_NAND
  16.     /* set exception handler */
  17.     rt_hw_exception_install(exception_handle);
  18. #endif

  19.     log_trace(LOG_TRACE_INFO"log trace initialized!\n");
  20. #endif /* RT_USING_LOGTRACE */

  21.     /* File System Initialization */
  22. #ifdef RT_USING_DFS
  23.     {
  24.         /* initialize the device file system */
  25.         dfs_init();

  26. #ifdef RT_USING_DFS_ELMFAT
  27.         /* initialize the elm chan FatFs file system*/
  28.         elm_init();
  29. #endif /* RT_USING_DFS_ELMFAT */

  30. #ifdef RT_USING_NFTL
  31.         nftl_attach("nand0");
  32.         if (dfs_mount("nand0", "/", "elm", 0, 0) == 0)
  33.         {
  34.             rt_kprintf("Mount FatFs file system to root, Done!\n");
  35.         }
  36.         else
  37.         {
  38.             rt_kprintf("Mount FatFs file system failed.\n");
  39.         }
  40. #endif

  41. #ifdef RT_USING_RAMFS
  42.         dfs_ramfs_init();
  43.         {
  44.             struct dfs_ramfs* ramfs;
  45.             ramfs = dfs_ramfs_create((rt_uint8_t*)EFM32_RAMFS_BEGIN, EFM32_RAMFS_SIZE);
  46.             if (ramfs != RT_NULL)
  47.             {
  48.                 if (dfs_mount(RT_NULL, "/ramfs", "ram", 0, ramfs) == 0)
  49.                 {
  50.                     rt_kprintf("Mount RAMDisk done!\n");
  51.                 }
  52.                 else
  53.                 {
  54.                     rt_kprintf("Mount RAMDisk failed.\n");
  55.                 }
  56.             }
  57.         }
  58. #endif
  59.     }
  60. #endif /* RT_USING_DFS */

  61.     /* Initialize I2C bus */
  62. #ifdef RT_USING_I2C
  63.     rt_i2c_core_init();
  64.     rt_hw_iicbus_init();
  65. #endif /* RT_USING_I2C */

  66.     /* Initialize RTC */
  67. #ifdef RT_USING_RTC
  68. #ifdef RT_USING_ALARM
  69.     rt_alarm_system_init();
  70. #endif

  71. #ifdef SH3H_USING_RX8025
  72.     rt_hw_rx8025_init(EFM32GG_IICBUS1_NAME);
  73. #endif

  74. #ifdef EFM32GG_USING_CHIP_RTC
  75.     rt_hw_rtc_init();
  76. #endif

  77. #ifdef SH3H_USING_RX8564
  78.     rt_hw_rx8564_init(EFM32GG_IICBUS0_NAME);
  79. #endif
  80. #endif /* RT_USING_RTC */

  81.     /* Initialize Watchdog */
  82. #ifdef RT_USING_WDT
  83. #ifdef EFM32GG_USING_CHIP_WDT
  84.     rt_hw_wdt_init();
  85. #endif

  86. #ifdef SH3H_USING_X4043_WDT
  87.     rt_hw_x4043_init(EFM32GG_IICBUS1_NAME);
  88. #endif

  89. #ifdef SH3H_USING_STM6822_WDT
  90.     rt_hw_stm6822_init();
  91. #endif

  92.     wdtmgr_init(SH3H_STM6822_WDT_NAME);
  93. #endif /* RT_USING_WDT */

  94. #ifdef RT_USING_LWIP
  95.     /* initialize lwip system */
  96.     eth_system_device_init();
  97.     lwip_system_init();
  98.     rt_kprintf("TCP/IP initialized!\n");

  99. #ifdef RT_LWIP_PPP
  100.     /* initialize ppp protocol */
  101.     pppInit();

  102.     modem_system_init();
  103.     modem_mg323_init();
  104.     modem_mc323_init();
  105.     modem_gl868_init();
  106. #endif /* RT_LWIP_PPP */

  107. #endif /* RT_USING_LWIP */

  108. #ifdef RT_USING_USB_DEVICE
  109.     rt_hw_usbd_init();

  110.     rt_usb_device_init("usbd");

  111. #if defined(RT_USING_LWIP) && defined(RT_USB_DEVICE_RNDIS)
  112.     {
  113.         extern void dhcpd_start(void);
  114.         extern void ssdp_start(void);
  115.         extern void telnet_srv(void);

  116.         dhcpd_start();
  117.         ssdp_start();
  118.         telnet_srv();
  119.     }

  120. #ifdef  RT_USING_WEBNET
  121.     {
  122.         extern void httpd_init(void);

  123.         httpd_init();
  124.     }
  125. #endif /* RT_USING_WEBNET */
  126. #endif // RT_USING_LWIP && RT_USB_DEVICE_RNDIS

  127. #endif /* RT_USING_USB_DEVICE */

  128. #ifdef RT_USING_PM
  129.     {
  130.         rt_pm_release(PM_RUNNING_MODE);
  131.         rt_pm_release(PM_SLEEP_MODE);
  132.         rt_pm_request(PM_TIMER_MODE);

  133.         rt_hw_plug_init();
  134.     }
  135. #endif /* RT_USING_PM */

  136.     /* init finsh */
  137. #ifdef RT_USING_FINSH
  138.     finsh_system_init();
  139.     finsh_set_device(RT_CONSOLE_DEVICE_NAME);
  140. #endif /* RT_USING_FINSH */
  141. }
复制代码
其中的宏条件、宏定义够多够乱!为了实现不同组件的独立性,不得不加无数的宏条件,“貌似”没什么好办法,但其实这些初始化是很规律性的,或者最简单的一点:编译进代码中的,需要初始化,不编译的自然不需要初始化。

原来有一个components.c,期望它可以进行各个组件的初始化,期望它能够做到:

  1. #include <components.h>

  2. static void rt_init_thread_entry(void *parameter)
  3. {
  4.     rt_components_init();
  5. }
复制代码
然后用户的代码自然也就清晰了,再乱也只是乱在rt_components_init()的实现里:

  1. /**
  2. * RT-Thread Components Initialization
  3. */
  4. void rt_components_init(void)
  5. {
  6. #ifdef RT_USING_MODULE
  7.     rt_system_module_init();
  8. #endif

  9. #ifdef RT_USING_FINSH
  10.     /* initialize finsh */
  11.     finsh_system_init();
  12.     finsh_set_device(RT_CONSOLE_DEVICE_NAME);
  13. #endif

  14. #ifdef RT_USING_LWIP
  15.     /* initialize lwip stack */
  16.     /* register ethernetif device */
  17.     eth_system_device_init();

  18.     /* initialize lwip system */
  19.     lwip_system_init();
  20. #endif

  21. #ifdef RT_USING_DFS
  22.     /* initialize the device file system */
  23.     dfs_init();

  24.     #ifdef RT_USING_DFS_ELMFAT
  25.     /* initialize the elm chan FatFS file system*/
  26.     elm_init();
  27.     #endif

  28.     #if defined(RT_USING_DFS_NFS) && defined(RT_USING_LWIP)
  29.     /* initialize NFSv3 client file system */
  30.     nfs_init();
  31.     #endif

  32.     #ifdef RT_USING_DFS_YAFFS2
  33.     dfs_yaffs2_init();
  34.     #endif

  35.     #ifdef RT_USING_DFS_UFFS
  36.     dfs_uffs_init();
  37.     #endif

  38.     #ifdef RT_USING_DFS_JFFS2
  39.     dfs_jffs2_init();
  40.     #endif

  41.     #ifdef RT_USING_DFS_ROMFS
  42.     dfs_romfs_init();
  43.     #endif

  44.     #ifdef RT_USING_DFS_DEVFS
  45.     devfs_init();
  46.     #endif
  47. #endif /* end of RT_USING_DFS */

  48. #ifdef RT_USING_NEWLIB
  49.     libc_system_init(RT_CONSOLE_DEVICE_NAME);
  50. #else
  51.     /* the pthread system initialization will be initiallized in libc */
  52.     #ifdef RT_USING_PTHREADS
  53.     pthread_system_init();
  54.     #endif
  55. #endif

  56. #ifdef RT_USING_RTGUI
  57.     rtgui_system_server_init();
  58. #endif

  59. #ifdef RT_USING_USB_HOST
  60.     rt_usb_host_init();
  61. #endif

  62.     return;
  63. }
复制代码
一样的宏条件,一样的糟糕的代码。特别是,一个巨大的问题:components.c属于rt-thread里的代码,如果每个用户都需要自己再去修改里面的代码,那么和把初始化代码放在application.c中没有本质的区别。

components.c的缺陷在于,依然不能摆脱宏条件的实现方式,另一个,用户不能够很方便的加入到初始化的序列中。

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

finsh shell的实现方式是把函数入口放到了一个独立的section中,然后再通过编译+链接过程中,获取这个section首地址、尾地址的形式,从而实现系统中所有输出符号的序列。同时为了能够在shell中按照函数名称来查找,在这个独立的section中实际填写的是:

  1. /* system call table */
  2. struct finsh_syscall
  3. {
  4.         const char*                name;                /* the name of system call */
  5. #if defined(FINSH_USING_DESCRIPTION) && defined(FINSH_USING_SYMTAB)
  6.         const char*                desc;                /* description of system call */
  7. #endif
  8.         syscall_func func;                /* the function address of system call */
  9. };
复制代码
这样一个结构体。记得论坛上好像有人提及到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的形式定义简化的宏:

  1. typedef int (*init_fn_t)(void);
  2. #define INIT_EXPORT(fn, level)        \
  3.         const init_fn_t __rt_init_##fn SECTION(".rti_fn."level) = fn

  4. #define INIT_BOARD_EXPORT(fn)                INIT_EXPORT(fn, "1")
  5. #define INIT_CPU_EXPORT(fn)                        INIT_EXPORT(fn, "2")
  6. #define INIT_DEVICE_EXPORT(fn)                INIT_EXPORT(fn, "3")
  7. #define INIT_COMPONENT_EXPORT(fn)        INIT_EXPORT(fn, "4")
  8. #define INIT_FS_EXPORT(fn)                        INIT_EXPORT(fn, "5")
  9. #define INIT_APP_EXPORT(fn)                        INIT_EXPORT(fn, "6")
复制代码
INIT_EXPORT宏用于输出一个函数到初始化序列中,相应的可以定义一些更简化的宏。

这样两个桩可以定义成:

  1. static int rti_start(void)
  2. {
  3.         return 0;
  4. }
  5. INIT_EXPORT(rti_start, "0");

  6. static int rti_end(void)
  7. {
  8.         return 0;
  9. }
  10. INIT_EXPORT(rti_end,"7");
复制代码
根据这两个桩的位置,简化的rt_components_init()函数就可以变成:

  1. void rt_components_init(void)
  2. {
  3.         const init_fn_t* fn_ptr;

  4.         for (fn_ptr = &__rt_init_rti_start; fn_ptr < &__rt_init_rti_end; )
  5.         {
  6.                 (*fn_ptr)();
  7.                 fn_ptr ++;
  8.         }
  9. }
复制代码
世界清静了!

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2013-6-22 23:07:25 | 显示全部楼层
本帖最后由 electrlife 于 2013-6-22 23:09 编辑

呵呵,这利用编译器段一般按字母排序的特性。uboot等也是这样处理的。

出0入0汤圆

发表于 2013-6-22 23:33:18 | 显示全部楼层
本帖最后由 McuPlayer 于 2013-6-22 23:34 编辑

支持一把RTT
去年一个PRJ打算用,后来因为计划变动项目被无限期Delay了
只做了驱动模块,基于RTT的多任务和事件,用DMA实现低速外设模块的异步读写操作,既不占用CPU时间又可以使得APP简洁

出0入0汤圆

发表于 2013-6-22 23:43:54 | 显示全部楼层
本帖最后由 geniuscode 于 2013-6-22 23:45 编辑

巧妙

出0入0汤圆

 楼主| 发表于 2013-6-23 08:08:06 | 显示全部楼层
这段代码已经上库了,以后的项目可以用这个初始化方式,不用再写一堆的大段初始化代码了。

后面再加log trace、crc模块,这些也是很有用的模块,特别是log trace

出0入0汤圆

发表于 2013-6-23 17:32:35 | 显示全部楼层
惯性思维,所以用宏理解简单,放开了,就当是跳出井口的青蛙...

出0入0汤圆

发表于 2013-6-24 18:55:33 | 显示全部楼层
下班了,做了个测试工程编译试试
工程编译后,从map文件找到相关部分内容:
InitFuncSym$$Base                        0x00000e18   Number         0  init_1.o(InitFuncSym)
    __rt_init_init_1                         0x00000e18   Data           4  init_1.o(InitFuncSym)
    __rt_init_init_2                         0x00000e1c   Data           4  init_2.o(InitFuncSym)
    __rt_init_init_3                         0x00000e20   Data           4  init_3.o(InitFuncSym)
    __rt_init_init_4                         0x00000e24   Data           4  init_4.o(InitFuncSym)
    __rt_init_init_5                         0x00000e28   Data           4  init_5.o(InitFuncSym)
    __rt_init_init_6                         0x00000e2c   Data           4  init_6.o(InitFuncSym)
    InitFuncSym$$Limit                       0x00000e30   Number         0  init_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)();
        }

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-6-24 20:04:13 | 显示全部楼层
amwox 发表于 2013-6-24 18:55
下班了,做了个测试工程编译试试
工程编译后,从map文件找到相关部分内容:
InitFuncSym$$Base                ...

这个只能适用于MDK吧。

出0入0汤圆

发表于 2013-6-25 09:10:20 | 显示全部楼层
增加了个IAR工程
参考RTT下的IAR工程,在ICF文件增加一行
keep { section InitFuncSym };
这行关系到以下信息是否生成.
在Debug/List下的map中有这样的描述
  .text              ro code  0x00000658   0x16  xprout.o [3]
  InitFuncSym                 0x00000670   0x14  <Block>
    InitFuncSym      const    0x00000670    0x4  init_1.o [1]
    InitFuncSym      const    0x00000674    0x4  init_2.o [1]
    InitFuncSym      const    0x00000678    0x4  init_4.o [1]
    InitFuncSym      const    0x0000067c    0x4  init_5.o [1]
    InitFuncSym      const    0x00000680    0x4  init_6.o [1]
  .rodata            const    0x00000684   0x10  init_1.o [1]
......
InitFuncSym$$Base     0x00000670         Data  Gb  - Linker created -
InitFuncSym$$Limit    0x00000684         Data  Gb  - Linker created -
IAR不太熟,模拟仿真一下


没有GCC,请有GCC的同学帮忙测试一下

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-6-25 10:29:30 | 显示全部楼层
IAR也支持$$?印象中,IAR支持的很不友好的

为了避免各种编译器的情况,所以在这里是用函数桩的形式,同时也避免如果系统中没定义相应的section,导致这个section是空的情况。

出0入0汤圆

发表于 2013-6-27 13:24:11 | 显示全部楼层
请问ffxz这次文档方面更新的多不多,现在git上的好像比之前的没多多少内容。

出0入0汤圆

发表于 2013-6-27 17:21:35 | 显示全部楼层
1. 这样做,初始化函数是不能带参数的,这个函数libc_system_init(RT_CONSOLE_DEVICE_NAME)是否有问题。
2. 能不能定义一个const的初始化函数指针的数组,如果没有编译的,其函数指针设为空指针,循环执行时不执行,这样是不是也可以,用户的初始化程序可以添加在后面。

出0入0汤圆

发表于 2013-6-28 01:24:48 | 显示全部楼层
还可以把系统任务和系统资源用这种形式来创建,
开发者只需定义需要的资源就好了, 不用去关心如何创建的.
RT_TASK_DEFINE("this task", thisTask, stackSize, prio, ....)
RT_MUTEX_DEFINE("this mutex", thisMutex, .....)
这样各个模块的应用工程师都可以很独立的开发,合并工作就是把代码放到一起编译就OK了,类似于依赖注入一样。

出0入0汤圆

发表于 2013-6-30 16:46:21 | 显示全部楼层
linux内核其实也是这么干的。

出0入0汤圆

发表于 2014-3-7 15:23:08 | 显示全部楼层
pppInit();

    modem_system_init();
    modem_mg323_init();
    modem_mc323_init();
    modem_gl868_init();
这些函数的实体定义在哪里呢?

出0入0汤圆

发表于 2014-3-8 14:45:20 | 显示全部楼层
tianqing324 发表于 2014-3-7 15:23
pppInit();

    modem_system_init();

除了pppInit(),其它都是具体项目的代码。

出0入0汤圆

发表于 2014-3-8 18:10:23 | 显示全部楼层
aozima 发表于 2014-3-8 14:45
除了pppInit(),其它都是具体项目的代码。

没有源码是吧,最近在搞gprs ppp
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-20 21:24

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表