RT-thread内核文档学习<一>内核基础
- Rtthread的初始化过程
纯属个人学习笔记,敬请指正
一、系统初始化
首先初始化会进入一个被Sub和super修饰的main函数,由于我从来没有见过于是就看了官方的文档,做出了如下总结。
附:
1.1 扩展补丁Sub和super的作用
** ∗ ∗ s u p e r ∗ ∗ ** super** ∗∗super∗∗ ∗ ∗ f o o 和 ∗ ∗ **foo 和 ** ∗∗foo和∗∗Sub$$**foo的使用方法如下
例如,如果它位于外部库中或ROM代码中,则无法修改现有符号。 在这种情况下,您可以使用 Super 和 sub 模式来修补现有符号。
修补函数foo()的定义:
** ∗ ∗ s u p e r ∗ ∗ ** super** ∗∗super∗∗$**foo():为原始未扩展的函数foo()。 使用它直接调用原始函数。
** ∗ ∗ S u b ∗ ∗ **Sub** ∗∗Sub∗∗$**foo():为的新函数而不是原始函数foo()。可以在此函数对super函数进行一些额外的处理。
注意:Sub和Super机制仅在静态链路时间工作,不能导入或导出到动态符号表中。
例如:以下示例显示了如何使用$ super KaTeX parse error: Can't use function '$' in math mode at position 2: 和$̲ sub ,以在调用传统功能foo()之前插入函数Extrafunc()的调用。
void Extrafunc(void);//额外的功能调用
void $ super $$ foo(void):/ *此函数称为原始foo()* /
void $ sub $$ foo(void)
{
Extrafunc(); / *是否有一些额外的设置工作* /
$ super $$ foo(); / *调用原始foo()函数* /
/*避免调用原始foo()函数
*省略$ super $$ foo(); 函数调用。
*/
}
首先以我的理解来描述一下这个补丁的作用:可以说这是在原有函数不改变的前提下,对函数的扩展或重写的一种方法
总结一下使用的方法:
- ①将原有函数用 ∗ ∗ s u p e r ∗ ∗ ** super** ∗∗super∗∗$ 这样的字段进行描述,表示将要进行额外的功能添加。
- ②重新定义一个与原函数同名的函数并使用** ∗ ∗ S u b ∗ ∗ **Sub** ∗∗Sub∗∗$**字段进行描述,表示此函数为扩展后的函数。
- ③在使用** ∗ ∗ S u b ∗ ∗ **Sub** ∗∗Sub∗∗$**描述的新函数中添加所需的功能代码,如果需要使用原有功能,那么加入原有函数的调用即可(描述字段不可省)。
好了我大致知道了这个特殊的main的运行机制后,我们继续探索系统的启动流程。按照官方的描述接下来是进入rtthread_startup()函数,我看到了如下代码,以及描述信息,大致可以猜测在这是衔接汇编和C的一个入口函数,如下enter函数:
接着就正真开始系统的启动开始:
int rtthread_startup(void)
{
rt_hw_interrupt_disable();
/* board level initialization
* NOTE: please initialize heap inside board initialization.
*/
rt_hw_board_init();
/* show RT-Thread version */
rt_show_version();
/* timer system initialization */
rt_system_timer_init();
/* scheduler system initialization */
rt_system_scheduler_init();
#ifdef RT_USING_SIGNALS
/* signal system initialization */
rt_system_signal_init();
#endif
/* create init_thread */
rt_application_init();
/* timer thread initialization */
rt_system_timer_thread_init();
/* idle thread initialization */
rt_thread_idle_init();
#ifdef RT_USING_SMP
rt_hw_spin_lock(&_cpus_lock);
#endif /*RT_USING_SMP*/
/* start scheduler */
rt_system_scheduler_start();
/* never reach here */
return 0;
}
- rt_hw_interrupt_disable();
系统中断失能,在汇编文件context_gcc.S中实现。
- rt_hw_board_init();
板载资源初始化,堆内存分配,HAL库MSP(中断优先级分组、嘀嗒定时器使能。引脚使能、端口时钟使能),系统时钟使能72M,以及系统元件初始化。
- rt_show_version();
在串口上位机中打印系统版本信息。
- rt_system_timer_init();
系统中的定时器容器中的定时器进行初始化。
- rt_system_scheduler_init();
线程调度器初始化,其中包括线程的初始化。
- rt_application_init();
main函数线程在此创建,应用线程初始化。
- rt_system_timer_thread_init();
定时器线程在此创建,创建此线程之前,又对定时器容器进行了一次初始化。
- rt_thread_idle_init();
空闲线程在此初始化。
- rt_system_scheduler_start();
系统调度开始。
我来总结一下到目前为止,系统都做了些什么工作:
- 开启了三个线程 main、timer、idle线程开启
- 板级的硬件也初始化完毕
那么接下来我们要做什么????或者说我们在实际使用操作系统的时候要怎么去做,以下是我个人的看法,具体还得自己去实践才知道好坏。
在这我进行了思考,我们要怎么把cubemx生成的硬件配置代码运用到系统中来。
首先我知道对于操作系统下有两种开发模式:
- 使用操作系统驱动库对mcu进行控制。drivers驱动
- 使用mcu原有库对mcu进行控制。Hal库
其次我知道操控一个外设需要的几步流程:
- 相关引脚时钟,以及IO口需要使能
- 相关外设功能需要配置,以及使能
- 配置完毕就可以使用了
那么我们需要在系统启动的那个阶段进行以上操作合适呢????我们慢慢探讨一下这个问题。
到这里系统内核的初始化已经全部启动完毕了,接下来就是三个线程对系统的调度了,套娃模式开启,线程中操作其他线程的创建和删除,多美妙呀!!!如图:(图示例中的其他线程,具体我不了解,在我的理解是可能在之前的初始化中,用户创建了新的线程,也可能是main函数中创建的线程也开始进入了系统的调度)
我们来看看main函数的入口函数的执行:
void main_thread_entry(void *parameter)
{
extern int main(void);
extern int $Super$$main(void);
#ifdef RT_USING_COMPONENTS_INIT
/* RT-Thread components initialization */
rt_components_init();
#endif
#ifdef RT_USING_SMP
rt_hw_secondary_cpu_up();
#endif
/* invoke system main function */
#if defined(__CC_ARM) || defined(__CLANG_ARM)
$Super$$main(); /* for ARMCC. */
#elif defined(__ICCARM__) || defined(__GNUC__)
main();
#endif
}
- rt_components_init();
系统元件的初始化。
- main();
进入main函数,进行用户应用层的操作。
接近尾声了,最后要做的就是在不同的线程中完成要做的任务,以及信息处理IPC的工作了,我们下次再进行讨论,时间不早了,我也该睡觉了Zzz!