RTT内核启动之rt_hw_board_init硬件板级初始化函数

RTT内核启动流程补充

在上一篇文章RTT内核启动流程-CSDN博客,只是简单的介绍了rtthread_startup函数中所调用的一些函数的作用,好有一个大概的认知,这篇文章想详细的介绍一下rt_hw_board_init这个函数的具体执行过程。

rt_hw_board_init函数

rtthread_startup函数所调用的第二个函数是rt_hw_board_init板级初始化函数,跳转到定义我们可以看到rt_hw_board_init函数的具体内容:
在这里插入图片描述

HAL_Init

第一个函数是HAL_Init函数(有可能会因为RTT版本的改变这里面的函数会发生一些变化),我们转到定义可以看到他的具体内容:
在这里插入图片描述

这句代码==#if (PREFETCH_ENABLE != 0)==是说如果定义了PREFETCH_ENABLE这个宏,那么会执行下面这些代码块:

#if defined(STM32F101x6) || defined(STM32F101xB) || defined(STM32F101xE) || defined(STM32F101xG) || \
    defined(STM32F102x6) || defined(STM32F102xB) || \
    defined(STM32F103x6) || defined(STM32F103xB) || defined(STM32F103xE) || defined(STM32F103xG) || \
    defined(STM32F105xC) || defined(STM32F107xC)

  /* Prefetch buffer is not available on value line devices */
  __HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif

这个代码块会对单片机的型号进行一些判断,因为有些单片机的型号是支持指令预取功能,而另一些是不支持的。所以这个代码块会在支持指令预取功能的型号上启动指令预取功能。
(也就是上面注释的第一行:Configure the Flash prefetch.)

有关于指令预取:
![[Pasted image 20240609182700.png]]

接着是HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);,正如上面的注释一样,进行中断优先级的分组,讲其设置为GROUP_4。

下面是HAL_InitTick(TICK_INT_PRIORITY);,这个函数的作用在注释中也提到了,配置系统滴答数来产生一个频率为1000Hz的中断,在目前这个阶段,时钟还没有被配置因此系统是被一个频率位16MHz的高速内部时钟信号驱动(HSI:High Speed Internal Clock)。
(后面可能会出一篇文章去具体介绍配置过程)

最后调用了HAL_MspInit();,函数去初始化一些底层硬件,在这里贴一张图,我也不太懂这个函数的具体作用以及内部运行的细节。
在这里插入图片描述

SystemClock_Config

跳转到定义可以发现这个函数紧挨着HAL_MspInit这个函数,这个函数的作用就是配置系统时钟信号
![[Pasted image 20240609185726.png]]

其实就是打通了红线这么一条路。

rt_system_heap_init

这个函数在此先不作介绍,会在后面研究RTT的内存管理的时候去介绍。跳转到定义根据函数的注释可以知道这个函数的作用是初始化操作系统的自己的堆内存。

rt_components_board_init

具体的函数内容如下:
![[Pasted image 20240609192157.png]]

根据注释可知,这个函数的作用是对板级组件的初始化。

如果我们这个宏RT_DEBUG_INIT的值为1,也就是使用调试功能(#if和#else都是C语言中的预处理指令,即根据条件的真假来选择编译哪些代码块),将执行以下代码块:

int result;
    const struct rt_init_desc *desc;
    for (desc = &__rt_init_desc_rti_board_start; desc < &__rt_init_desc_rti_board_end; desc ++)
    {
        rt_kprintf("initialize %s", desc->fn_name);
        result = desc->fn();
        rt_kprintf(":%d done\n", result);
    }

![[Pasted image 20240609193003.png]]

但是在跳转定义的时候却发现这个结构体没有被定义,于是我尝试在rtdef.h(这个头文件中保存了绝大部分的定义)文件中ctrl+F寻找这个结构体,找到了这个结构体的原型
![[Pasted image 20240609193521.png]]

因为这个结构体前面有一个#if RT_DEBUG_INIT条件预处理指令,而在我下载的这个版本的RTT中,这个宏的值被定为了0,所以编译器在编译的时候也就自然不会去编译这个结构体。

在这个结构体内部,有一个字符指针,指针名为fn_name,并且还有一个函数指针,其原型在第二行:typedef int (*init_fn_t)(void);

下面定义了一个宏:

#define INIT_EXPORT(fn, level)                                                       \
            const char __rti_##fn##_name[] = #fn;                                            \
            RT_USED const struct rt_init_desc __rt_init_desc_##fn SECTION(".rti_fn."level) = \
            { __rti_##fn##_name, fn};

这个宏的作用是:
![[Pasted image 20240609200450.png]]

如果有兴趣去学习这个宏具体为什么能实现这个功能,可以看一下这篇文章:
RT-Thread INIT_EXPORT宏-CSDN博客

在这个函数中定义出来的rt_init_desc结构体实际上是对板级组件初始化函数的一个描述(desc实际上是description,也就是对rt_init函数的描述)。接下来就是一个for循环,for循环的第一步先对我们定义出来的desc结构体进行了一个初始化赋值,但是**__rt_init_desc_rti_board_start**这是一个什么东西呢?

我们在components.c文件中可以可以找到这样一些函数:
![[Pasted image 20240611131746.png]]

如果我们尝试将自己代入INIT_EXPORT(rti_board_start, “0.end”); 这个宏的执行过程的话,它会被展开这样:

const char __rti_rti_board_start_name[] = #rti_board_start;
RT_USED const struct rt_init_desc __rt_init_desc_rti_board_start SECTION(".rti_fn.0.end") = {__rti_rti_board_start_name, rti_board_start};

可以看出,rti_board_start这个函数指针和rti_board_start这个字符串被打包成了一个名为
__rt_init_desc_rti_board_start的结构体,并且这个结构体题被放在了.rti_fn.0.end这个段中,而在这个段中,存放了其他板级初始化的结构体,并且这个结构体是所有结构体的“头”,如果我们得到了这个结构体的地址,我们也就可以继续寻址访问其他初始化函数描述的结构体。

所以在这个for循环中,desc作为游标结点,被赋值为了这个段的首地址,当地址偏移到 __rt_init_desc_rti_board_end时,结束这个循环,在循环的过程中会打印正在初始化的函数,并在结束调用初始化函数后返回一个状态,这个状态也会在调试窗口被打印出来。

如果我们这个宏RT_DEBUG_INIT的值为0,也就是不使用调试功能,INIT_EXPORT这个宏会做出对应的改变
![[Pasted image 20240611134847.png]]

这时他不在是一个结构体,而只是一个单纯的函数指针。
![[Pasted image 20240611134952.png]]

但是这两个for循环的本质都是一样的,就是自动调用被放在段中的初始化函数。

rt_console_set_device(RT_CONSOLE_DEVICE_NAME); 这个函数将会放在其他文章中去讲解。

  • 21
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值