博客
关于我
rt-thread 使用心得
阅读量:496 次
发布时间:2019-03-07

本文共 2987 字,大约阅读时间需要 9 分钟。

rt-thread 使用心得

最近做了一个项目,接触到了 rt-thread 这款国产实时操作系统,进行了简单的配置之后就能够在板子上面调试,确实很方便!

在配置过程中,我遇到了很多有趣的挑战,尤其是与 rt-thread 的初始化机制相关的内容。相比于常见的嵌入式实时操作系统,rt-thread 的初始化方式有着显著的不同。

rt-thread 驱动与组件初始化

rt-thread 系统驱动与组件的初始化与常见的嵌入式实时操作系统有较大区别。rt-thread通过将初始化函数地址以不同的初始化级别排序后存储到初始化段中,在段的前后设置锚点,通过遍历获取锚点间存储的函数指针,依次执行来完成初始化工作。

初始化工作可以分为组件板级初始化与组件系统级初始化两种类别,这两种类别的初始化函数指针依次存放。以 rt-thread 中的 component.c 源代码为例,初始化函数的执行顺序如下:

a. rti_start -> 0

b. BOARD_EXPORT -> 1
c. rti_board_end -> 1.end
d. DEVICE_EXPORT -> 2
e. COMPONENT_EXPORT -> 3
f. FS_EXPORT -> 4
g. ENV_EXPORT -> 5
h. APP_EXPORT -> 6
i. rti_end -> 6.end

在我的项目的 map 文件中与初始化函数段相关的数据如下:

0x60033030                __rt_init_start = . *(SORT(.rti_fn*)) .rti_fn.0      0x60033034                __rt_init_board_start .rti_fn.1      0x60033038                __rt_init_imxrt_hw_uart_init .rti_fn.1      0x6003303c                __rt_init_rt_hw_pin_init .rti_fn.1      0x60033040                __rt_init_rt_hw_spi_bus_init .rti_fn.1.end  0x60033044                __rt_init_rti_board_end .rti_fn.2      0x60033048                __rt_init_dfs_init .rti_fn.2      0x6003304c                __rt_init_lwip_system_init .rti_fn.2      0x60033050                __rt_init_rt_mmcsd_core_init .rti_fn.3      0x60033054                __rt_init_rt_hw_hp_rtc_init .rti_fn.3      0x60033058                __rt_init_rt_hw_i2c_init .rti_fn.3      0x6003305c                __rt_init_rt_hw_lcd_init .rti_fn.3      0x60033060                __rt_init_imxrt_mci_init .rti_fn.3      0x60033064                __rt_init_rt_hw_imxrt_eth_init .rti_fn.4      0x60033068                __rt_init_elm_init .rti_fn.4      0x6003306c                __rt_init_libc_system_init .rti_fn.4      0x60033070                __rt_init_rt_i2c_core_init .rti_fn.6      0x60033074                __rt_init_finsh_system_init .rti_fn.6.end  0x60033078                __rt_test_init_rti_end  0x6003307c                __rt_init_end = .

切换到链接脚本

从链接脚本可以看出以下几点:

  • __rt_init_start 是初始化函数段的开始地址,__rt_init_end 是该段的结束地址。
  • KEEP(*(SORT(.rti_fn*))) 旗语的作用:
  • 强制链接器保留以 rti_fn 为前缀的所有段。
  • 对不同 .o 文件中的段进行排序,这一点尤为重要。

在构建第一个可以调试的工程时,我发现链接脚本中缺少了 KEEP(*(SORT(.rti_fn*))) 这条语句,导致无法正常完成初始化工作,系统一运行便进入断言。如果你在使用 rt-thread 时遇到断言问题,可以通过生成的 map 文件中搜索 __rti_fn 进行核查,确保相关段存在且排列有序。如果搜索不到,或者排列混乱,很可能是链接脚本中缺少了相应的语句。

为什么需要排序?

我曾经对这背后的逻辑感到困惑,直到深入研究了初始化部分的代码。

两个主要的初始化函数如下:

void rt_components_board_init(void)	{      for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)	{          (*fn_ptr)();	}  }  void rt_components_init(void)	{      for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++)	{          (*fn_ptr)();	}  }

rt_components_board_init 是组件的板级初始化函数,依次遍历 __rt_init_rti_board_start__rt_init_rti_board_end 之间的函数地址并执行。如果没有进行排序,这些函数的地址可能不在这两个锚点之间,造成无法获取到相应的函数地址,进而无法完成初始化工作。

rt_components_init 是组件的系统级初始化函数,原理与上述函数相同,这里就不赘述了。

入口函数设定

在 rt-thread 系统中,系统并没有像常规嵌入式系统那样使用 main 函数作为入口。而是通过 entry 函数作为系统执行入口。在使用 arm-none-eabi-gcc 编译时,可以通过添加 -eentry 参数来以 entry 代替 main,这是一个不错的技巧!

转载地址:http://ugdjz.baihongyu.com/

你可能感兴趣的文章
Java 中的多态详解
查看>>
交换二叉树中每个结点的左孩子和右孩子~2020.8.13~学习笔记
查看>>
Java常用包系列--JWT
查看>>
Linux命令--文件命令
查看>>
并发相关问题
查看>>
Linux应用编程示例
查看>>
flutter 基于Timer实现的倒计时以及周期性任务
查看>>
Java时间类Date全解析
查看>>
Glide4实现网络图片加载进度监听
查看>>
MFC编辑框中追加文本
查看>>
一季度销售暴增210%,负债持续改善的金辉控股,有多少水分?
查看>>
linux系统下双屏显示
查看>>
基于linux的VScode开发
查看>>
关于高斯模糊核
查看>>
实验笔记之——octave layer(4路数据)
查看>>
PDF.js —— vue项目中使用pdf.js显示pdf文件(流)
查看>>
vue源码解析 —— 数据绑定
查看>>
JS单线程的理解
查看>>
vue解决数据更新,界面未更新的方法
查看>>
我用wxPython搭建GUI量化系统之wx.grid实现excel功能
查看>>