RT-Thread启动流程?RT-Thread如何支持不同开发板?

启动流程
一个开发板上的rt-thread的启动流程可能是首先从bsp​当中链接脚本指定的startup_xxx.s​中的入口函数(entry)或者复位异常处理函数(resethandler)开始运行,这部分我们在讲​bsp​支持时会详细讲解。
之后跳入entry​函数(gcc,使用不同编译器会进入不同的函数)执行,这里也可以跳入用户自己的main函数,但是需要用户自己完成rtthread_startup​的工作。
这个函数十分简单,首先先关中断(关中断操作由cpu支持部分提供),然后进入rt-thread的全局初始化中。
#if defined (__cc_arm)
extern int super$main(void);
/* re-define main function /
int sub$main(void)
{
rt_hw_interrupt_disable();
rtthread_startup();
return 0;
}
#elif defined( iccarm )
extern int main(void);
/ __low_level_init will auto called by iar cstartup /
extern void __iar_data_init3(void);
int __low_level_init(void)
{
// call iar table copy function.
__iar_data_init3();
rt_hw_interrupt_disable();
rtthread_startup();
return 0;
}
#elif defined( gnuc )
extern int main(void);
/ add -eentry to arm-none-eabi-gcc argument */
int entry(void)
{
rt_hw_interrupt_disable();
rtthread_startup();
return 0;
}
#endif
​rtthread_startup​是启动流程当中的关键,首先rtthread_startup​会先调用rt_hw_board_init​,这个函数也由bsp支持部分提供,一般来说主要完成例如初始化中断向量表、系统时钟的初始化,设置输出控制台,同时初始化系统堆内存。
紧接着会调用rt_show_version​打印rt-thread内核的系统版本信息,其中主要是利用控制台(rt_printf​)进行输出,通常来说是用户在bsp支持中提供串口的注册来实现的。以rt-thread simulator 例程来说,会通过设备驱动一路调用到bsp支持部分提供的串口输出。
void rt_kprintf(const char *fmt, ...) ->
rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) ->
static rt_size_t rt_serial_write(struct rt_device *dev, rt_off_t pos, const void *buffer, rt_size_t size) ->
rt_inline int _serial_poll_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length) ->
static int stm32_putc(struct rt_serial_device *serial, char c)然后会调用rt_system_timer_init​初始化系统定时器链表,这个函数比较简单,主要就是数据结构进行初始化。
紧接着会调用rt_system_scheduler_init​初始化rt-thread系统调度器相关的数据结构:
线程优先级链表:每一个优先级对应一个链表,通过rt_thread​结构中的tlist​成员来进行相同优先级线程的连接
当前线程优先级
当前线程控制块
​rt_thread_ready_priority_group​中的每一位代表1个优先级,该位为1表示该优先级下有就绪线程,该位为0表示该优先级下没有就绪线程
僵尸线程链表
void rt_system_scheduler_init(void)
{
register rt_base_t offset;
rt_scheduler_lock_nest = 0;
rt_debug_log(rt_debug_scheduler, (start scheduler: max priority 0x%02xn,
rt_thread_priority_max));
for (offset = 0; offset 32
/ initialize ready table /
rt_memset(rt_thread_ready_table, 0, sizeof(rt_thread_ready_table));
#endif
/ initialize thread defunct */
rt_list_init(&rt_thread_defunct);
}
接下来会调用rt_application_init​初始化一个main主线程(并不会马上运行),主要完成组件的初始化以及多核的处理,最后跳入用户的main​函数
完成组件初始化的实现与rt_components_board_init​类似,在bsp支持部分讲解。
void rt_application_init(void)
{
rt_thread_t tid;
#ifdef rt_using_heap
tid = rt_thread_create(main, main_thread_entry, rt_null,
rt_main_thread_stack_size, rt_main_thread_priority, 20);
rt_assert(tid != rt_null);
#else
rt_err_t result;
tid = &main_thread;
result = rt_thread_init(tid, main, main_thread_entry, rt_null,
main_stack, sizeof(main_stack), rt_main_thread_priority, 20);
rt_assert(result == rt_eok);
/* if not define rt_using_heap, using to eliminate the warning */
(void)result;
#endif
rt_thread_startup(tid);
}
void main_thread_entry(void parameter)
{
extern int main(void);
extern int super$main(void);
/ rt-thread components initialization /
rt_components_init();
/ invoke system main function /
#if defined (__cc_arm)
super$main(); / for armcc. */
#elif defined( iccarm ) || defined( gnuc )
main();
#endif
}
void rt_components_init(void)
{
#if rt_debug_init
int result;
const struct rt_init_desc *desc;
rt_kprintf(do components intialization.n);
for (desc = &__rt_init_desc_rti_board_end; desc fn_name);
result = desc->fn();
rt_kprintf(:%d donen, result);
}
#else
const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_board_end; fn_ptr fn();
rt_kprintf(:%d donen, result);
}
#else
const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < & **rt_init_rti_board_end; fn_ptr++)
{
(fn_ptr)();
}
#endif
}
​rt_used​: attribute ((used))​,标识不允许编译器进行优化
​init_fn_t​:typedef int (init_fn_t)(void)​,函数指针
​##​:连接符
​section​: attribute ((section(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​static int rti_board_start(void)
{
return 0;
}
init_export(rti_board_start, 0.end);
#define init_export(fn, level) rt_used const init_fn_t _ rt_init ##fn section(.rti_fn.level) = fn
​rt_board_end​同理,所以rt_components_board_init​的含义则为执行__rt_init_rti_board_start​到__rt_init_rti_board_end​之间函数
指定节.rti_fn.1​,根据链接器节放置规则,将放置在.rti_fn.0.end​节和.rti_fn.1.end​节之间。
#define init_board_export(fn) init_export(fn, 1)
所以rt-thread提供了另一个宏,它的主要作用就是用来在初始化硬件时调用相应的函数。所以一些外设驱动初始化都展开了这个宏。以rt-thread simulator 例程为例:
init_board_export(rt_hw_usart_init);
init_board_export(rt_hw_pin_init);
总的来说,一个基本的bsp主要任务是建立让操作系统运行的基本环境,所以大致需要完成的主要工作是:
初始化cpu内部寄存器,设定ram工作时序。
实现时钟驱动及中断控制器驱动,完善中断管理。
实现串口和 gpio 驱动。
初始化动态内存堆,实现动态堆内存管理。
cpu支持
这部分官方文档很详细,可参考内核移植 (rt-thread.org)
在嵌入式领域有多种不同 cpu 架构,例如 cortex-m、arm920t、mips32、risc-v 等等。为了使 rt-thread 能够在不同 cpu 架构的芯片上运行,rt-thread 提供了一个 libcpu 抽象层来适配不同的 cpu 架构。libcpu 层向上对内核提供统一的接口,包括全局中断的开关,线程栈的初始化,上下文切换等。
rt-thread 的 libcpu 抽象层向下提供了一套统一的 cpu 架构移植接口,这部分接口包含了全局中断开关函数、线程上下文切换函数、时钟节拍的配置和中断函数、cache 等等内容。下表是 cpu 架构移植需要实现的接口和变量。
libcpu 移植相关 api

小米众筹上架云麦智能训练手表 主打无器械训练识别捕捉售价699元
毫米波RIS研究测试方案:一站式助力工程师探索高频通信未来
创维Q91系列大屏8K电视 将开启家庭观影新体验
直流减速电机的选型
大立光举行线上法说会公布第三季度财报,以163.33亿元创下历史次高
RT-Thread启动流程?RT-Thread如何支持不同开发板?
封装的作用是什么
Bruce:ARM+Linux代表了嵌入式未来趋势
利用Matter协议实现标准化:全新无线协议为智能设备互联构筑基石
PS5开发机真机照曝光 采用“深V”造型
微软新云数据中心区域设在卡塔尔,预计2021年投入使用
谷歌将在车载互联、云计算上为福特提供支持
美国采取措施加速5G军用测试
艾迈斯欧司朗推出一次性医用内窥镜NanEyeM摄像头模组
技术与计算负载如何驱动多芯片系统
在Manning带领下,斯坦福AI实验室将走出一条怎样的道路?
人工智能和网络安全防范新出现的威胁
基于比较器的过压保护电路设计方案
环网柜共箱和不共箱的区别 环网柜基本组成 环网柜和母联柜的区别
华硕无畏Pro15 2024轻薄本发布,酷睿Ultra和2.8K OLED屏助力卓越PG游戏性能