剖析RT-Thread中console与finsh组件实现(1)

1.版本

本文代码版本在这里插入图片描述
代码IDE版本:Keil5
开发板:STM32F103ZET6

2.代码流程

我们从.s文件开始
根据芯片,我们的启动代码为startup_stm32f103xe.s,如下图我们可以看到和其他启动代码并没有区别,在先进入SystemInit函数初始化时钟后,经过一系列操作,最后跳转main函数
在这里插入图片描述
但在这里,RTT使用了MDK的一个"补丁"功能,给main函数打了一个补丁。如下图。这里__CC_ARM即通过MDK的编译器编译时,会编译下面的函数。文件名components.c
在这里插入图片描述
tips:

当MDK链接器链接代码链接到a()时,如果存在$Sub$$a(),则会把a()替换为$Sub$$a(),
而在遇到$Super$$a()时,则会把它替换为a()

官方详解如下:
ARM Compiler v5.06 for µVision armlink User Guide
所以可知,其实在启动文件结束后,其实代码到了$Sub$$main()里,函数rt_hw_interrupt_disable很明显是关闭中断,我们不去深究。我们仔细看一下rtthread_startup这个函数,代码如下,文件名components.c
在这里插入图片描述
其中main函数包含在rt_application_init里面的main任务的入口函数里,不过这里我们不作详细展开。我们看一下串口的初始化,它包含在rt_hw_board_init函数里,这个函数主要包含了所有板级初始化,代码如下,文件名:board.c
在这里插入图片描述

这里我们可以看一下所有外设包含串口是怎么初始化的。代码如下。文件名components.c
在这里插入图片描述
可以看到初始化代码非常简单,只有一个for循环,起始就是执行了从 __rt_init_rti_board_start__rt_init_rti_board_end 的所有函数指针所指向的函数,起始函数地址为 __rt_init_rti_board_start 所指内容。查看 __rt_init_rti_board_start__rt_init_rti_board_start 定义可以看到:
在这里插入图片描述
这里 INIT_EXPORT() 宏定义如下,文件名:rtdef.h
在这里插入图片描述
其中 RT_USED 宏定义如下:
在这里插入图片描述
作用即标记该变量是已使用的,即使未被使用,链接器也不得删除。(链接器会自动删除未被使用的段)
另外 init_fn_t 是一个入参为空,返回为int的函数指针。定义如下:
在这里插入图片描述
而宏定义 SECTION(".rti_fn."level) 定义如下:
在这里插入图片描述
attribute((section(x))) 为将该变量存于名称为x的段内
所以其实 INIT_EXPORT(rti_board_start, “0.end”); 这句的意思就是:

__attribute__((used)) const init_fn_t __rt_init_rti_board_start __attribute__((section(.rti_fn.0.end))) = rti_board_start

也就是把等于rti_board_start的函数指针__rt_init_rti_board_start放在段名为.rti_fn.0.end里。
同理可得
在这里插入图片描述
上面这句的意思就是
把等于rti_board_end的函数指针__rt_init_rti_board_end放在段名为.rti_fn.1.end里
所以初始化过程就是历遍段名为 .rti_fn.0.end 到段名为 .rti_fn.1.end 之间的所有函数并运行。所以在这两者之间到底有哪些函数?
答案是所有调用 插入这部分段 的宏的函数。也就是那个函数调用宏插入到这部分段里了,那部分函数就会在这个for循环里被执行。而插入的宏为下图,文件名:rtdef.h
在这里插入图片描述
可以看到这里有很多宏,分别可以插入到后缀为1到6的段。
tips
这里没有找到MDK链接器的相关内容,笔者猜测链接器链接时会根据名称排序,所以段名后缀为**.1**的会被插入到 .1.end 之前,而段名后缀为 .2.6 的会被插入到 .6.end 之前,这个链接顺序为隐式规则。
所以在文件名:drv_usart.c中可以看到
在这里插入图片描述
rt_hw_usart_init函数里面包含了串口1的初始化,这里RTT使用了类似LINUX的设备驱动分离框架,不作展开。可以简单的理解为现在USART1已经初始化完成可以使用了。
我们回到 rt_hw_board_init 函数,它里面还剩下一个 rt_console_set_device 没有分析,从名字就可以看出这是函数是为了设置控制台(物理层终端),所以它要放在所有外设初始化完成后。函数代码如下:
在这里插入图片描述

可以看到入参为name字符串,此处name正是串口1:
在这里插入图片描述
这里 rt_device_find 是寻找与入参同名设备,函数内容如下:
在这里插入图片描述
函数里面的 rt_object_get_information 函数内容比较简单就是历遍结构体数组,对比数组里type元素是否与入参相同,相同则返回该结构体指针。代码如下:
在这里插入图片描述
我们知道前面初始化已经把串口1初始化完成了,那列表内肯定存在一个名为usart1的设备了,所以肯定可以找到,那它返回的设备信息块(结构体)到底是什么呢?

下一章:剖析RT-Thread中finsh组件实现(2)

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值