在keil 中使用__attribute__关键字实现静态加载

实现各个线程或者各个模块自动初始化加载,避免在main中手动调用各种init()函数,各个模块间可实现高度解藕

点击查看代码
/* 宏定义如下 */

#define SECTION_DEF(_level)             __attribute__((used,__section__("LoadSection_"_level)))
#define SysLoad_Peripher1(func)         const SysLoad_Init_t SysLoad_Peripher1_##func SECTION_DEF("Peripher_1") = {func}
#define SysLoad_Peripher2(func)         const SysLoad_Init_t SysLoad_Peripher2_##func SECTION_DEF("Peripher_2") = {func}
#define SysLoad_Peripher3(func)         const SysLoad_Init_t SysLoad_Peripher3_##func SECTION_DEF("Peripher_3") = {func}
#define SysLoad_APP(init, app, tcb)     const SysLoad_App_t SysLoad_APP_##app SECTION_DEF("APP_1") = {init, app, {0, NULL, 56}}


/* 调用代码如下 */
void    perFunc1(void)
{
    printk(DBG_LEVEL_INFO, "fnc1\r\n");
    return;
}
void    perFunc2(void)
{
    printk(DBG_LEVEL_INFO, "fnc2\r\n");
    return;
}
void    perFunc3(void)
{
    printk(DBG_LEVEL_INFO, "fnc3\r\n");
    return;
}

SysLoad_Peripher3(perFunc2);
SysLoad_Peripher3(perFunc3);
SysLoad_Peripher3(perFunc1);

SysLoad_Peripher2(perFunc2);
SysLoad_Peripher2(perFunc3);
SysLoad_Peripher2(perFunc1);

SysLoad_Peripher1(perFunc2);
SysLoad_Peripher1(perFunc3);
SysLoad_Peripher1(perFunc1);

编译后的.map文件中可发现编译器分配各段地址时是以段的字符来排序的.同一段内的数据是以数据定义的顺序来分配。如下:

点击查看代码
LoadSection_Peripher_1$$Base             0x00006ad8   Number         0  main.o(LoadSection_Peripher_1)

    SysLoad_Peripher1_perFunc2               0x00006ad8   Data           4  main.o(LoadSection_Peripher_1)

    SysLoad_Peripher1_perFunc3               0x00006adc   Data           4  main.o(LoadSection_Peripher_1)

    SysLoad_Peripher1_perFunc1               0x00006ae0   Data           4  main.o(LoadSection_Peripher_1)

    LoadSection_Peripher_1$$Limit            0x00006ae4   Number         0  main.o(LoadSection_Peripher_1)

    LoadSection_Peripher_2$$Base             0x00006ae4   Number         0  main.o(LoadSection_Peripher_2)

    SysLoad_Peripher2_perFunc2               0x00006ae4   Data           4  main.o(LoadSection_Peripher_2)

    SysLoad_Peripher2_perFunc3               0x00006ae8   Data           4  main.o(LoadSection_Peripher_2)

    SysLoad_Peripher2_perFunc1               0x00006aec   Data           4  main.o(LoadSection_Peripher_2)

    LoadSection_Peripher_2$$Limit            0x00006af0   Number         0  main.o(LoadSection_Peripher_2)

    LoadSection_Peripher_3$$Base             0x00006af0   Number         0  main.o(LoadSection_Peripher_3)

    SysLoad_Peripher3_perFunc2               0x00006af0   Data           4  main.o(LoadSection_Peripher_3)

    SysLoad_Peripher3_perFunc3               0x00006af4   Data           4  main.o(LoadSection_Peripher_3)

    SysLoad_Peripher3_perFunc1               0x00006af8   Data           4  main.o(LoadSection_Peripher_3)

    LoadSection_Peripher_3$$Limit            0x00006afc   Number         0  main.o(LoadSection_Peripher_3)

自动加载或初始化方式如下:

点击查看代码
/* 测试发现如果代码中未定义某个段的话就不能引用连接脚本中的$$Length标识符,编译器会报错。
  但是可以引用$$Base和$$Limit两个标识符,编译器只会报警忽略两个外部变量。原因不明?? 
  但是不有关系,只要有$$Base和$$Limit两个变量就可以定位段的位置和长度了 */

extern int LoadSection_Peripher_3$$Base;
extern int LoadSection_Peripher_3$$Limit;
//extern int LoadSection_Peripher_3$$Length;

extern int LoadSection_Peripher_2$$Base;
extern int LoadSection_Peripher_2$$Limit;
//extern int LoadSection_Peripher_2$$Length;

extern int LoadSection_Peripher_1$$Base;
extern int LoadSection_Peripher_1$$Limit;
//extern int LoadSection_Peripher_1$$Length;


{
        SysLoad_Init_t *pt;

        base = (uint32_t)(&LoadSection_Peripher_1$$Base);
        limit = (uint32_t)(&LoadSection_Peripher_1$$Limit);
        length = (limit - base)/sizeof(SysLoad_Init_t);
        printk(DBG_LEVEL_INFO, "base1=0x%08x, limit1=0x%08x, length1=0x%08x\r\n", base, limit, length);

        pt = (SysLoad_Init_t *)base;
        for(uint32_t i= 0; i < length; i++)
        {
            pt->init();
            pt++;
        }

  }
打印结果如下:
点击查看代码
INFO:base1=0x00006ad8, limit1=0x00006ae4, length1=0x00000003
INFO:fnc2
INFO:fnc3
INFO:fnc1
posted @ 2025-12-06 23:03  Jonathan_Ray  阅读(19)  评论(0)    收藏  举报