0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

冬至子 来源:dejavudwh 作者:dejavudwh 2023-08-10 15:29 次阅读

启动流程
一个开发板上的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 < RT_THREAD_PRIORITY_MAX; offset ++)
{
rt_list_init(&rt_thread_priority_table[offset]);
}
rt_current_priority = RT_THREAD_PRIORITY_MAX - 1;
rt_current_thread = RT_NULL;
/* initialize ready priority group /
rt_thread_ready_priority_group = 0;
#if RT_THREAD_PRIORITY_MAX > 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 < &__rt_init_desc_rti_end; desc ++)
{
rt_kprintf("initialize %s", 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 < &__rt_init_rti_end; fn_ptr ++)
{
(*fn_ptr)();
}
#endif
}

然后,rt_system_timer_thread_init​主要是初始化软件定时器的列表,并且创建软件定时器线程。而rt_thread_idle_init​创建空闲线程,在系统没有任何用户线程调度的时候,就会被调度起来,这个空闲线程主要是检查系统有没有已经消亡的线程,如果有,则把消亡线程的资源进行回收,如果系统使能了电源管理,则会让系统进行低功耗模式。

最后通过rt_system_scheduler_start​开启调度器。

BSP支持
首先需要提供startup_xxx.S​类似的启动文件,一般来说可能包含中断向量表以及默认的中断服务函数,以及选择入口函数,一般可能为_start​或者ResetHandler​。

AREA RESET, DATA, READONLY
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
; External Interrupts
DCD WWDG_IRQHandler ; Window Watchdog
DCD PVD_IRQHandler ; PVD through EXTI Line detect
DCD TAMPER_IRQHandler ; Tamper
DCD RTC_IRQHandler ; RTC
DCD FLASH_IRQHandler ; Flash
DCD RCC_IRQHandler ; RCC
DCD EXTI0_IRQHandler ; EXTI Line 0
DCD EXTI1_IRQHandler ; EXTI Line 1
DCD EXTI2_IRQHandler ; EXTI Line 2
DCD EXTI3_IRQHandler ; EXTI Line 3
DCD EXTI4_IRQHandler ; EXTI Line 4
DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1
DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2
DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3
DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4
DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5
DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6
DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7
DCD ADC1_2_IRQHandler ; ADC1_2
DCD USB_HP_CAN1_TX_IRQHandler ; USB High Priority or CAN1 TX
DCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0
DCD CAN1_RX1_IRQHandler ; CAN1 RX1
DCD CAN1_SCE_IRQHandler ; CAN1 SCE
DCD EXTI9_5_IRQHandler ; EXTI Line 9..5
DCD TIM1_BRK_IRQHandler ; TIM1 Break
DCD TIM1_UP_IRQHandler ; TIM1 Update
DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation
DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare
DCD TIM2_IRQHandler ; TIM2
DCD TIM3_IRQHandler ; TIM3
DCD 0 ; Reserved
DCD I2C1_EV_IRQHandler ; I2C1 Event
DCD I2C1_ER_IRQHandler ; I2C1 Error
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SPI1_IRQHandler ; SPI1
DCD 0 ; Reserved
DCD USART1_IRQHandler ; USART1
DCD USART2_IRQHandler ; USART2
DCD 0 ; Reserved
DCD EXTI15_10_IRQHandler ; EXTI Line 15..10
DCD RTC_Alarm_IRQHandler ; RTC Alarm through EXTI Line
DCD USBWakeUp_IRQHandler ; USB Wakeup from suspend
__Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors
AREA |.text|, CODE, READONLY
; Reset handler routine
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
bsp支持主要提供对开发板的硬件初始化(通过rt_hw_board_init​向上提供接口)以及各类外设的驱动。

以RT-Thread Simulator 例程为例:rt_hw_board_init​中主要完成HAL库的初始化、时钟配置和RT-Thread系统堆的初始化,以及rt_components_board_init​会完成硬件的初始化。

#define RT_DEBUG_INIT 0
/**

  • RT-Thread Components Initialization for board
    */
    void rt_components_board_init(void)
    {
    #if RT_DEBUG_INIT
    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 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
1.jpg

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 电源管理
    +关注

    关注

    112

    文章

    6015

    浏览量

    141109
  • 定时器
    +关注

    关注

    23

    文章

    3148

    浏览量

    112048
  • RT-Thread
    +关注

    关注

    31

    文章

    1149

    浏览量

    38906
  • gcc编译器
    +关注

    关注

    0

    文章

    78

    浏览量

    3233
  • 调度器
    +关注

    关注

    0

    文章

    95

    浏览量

    5161
收藏 人收藏

    评论

    相关推荐

    RT-Thread记录(一、版本开发环境及配合CubeMX)

    RT-Thread 学习记录的第一篇文章,RT-Thread记录(一、RT-Thread 版本、RT-Thread Studio开发环境 及
    的头像 发表于 06-20 00:28 4489次阅读
    <b class='flag-5'>RT-Thread</b>记录(一、版本<b class='flag-5'>开发</b>环境及配合CubeMX)

    RT-Thread记录(三、RT-Thread线程操作函数)

    讲完了RT-Thread开发环境,启动流程启动以后当然是开始跑线程了,那么自然我们得学会如何创建线程以及线程的有关操作。
    的头像 发表于 06-20 00:31 5571次阅读
    <b class='flag-5'>RT-Thread</b>记录(三、<b class='flag-5'>RT-Thread</b>线程操作函数)

    RT-Thread记录(二、RT-Thread内核启动流程

    在前面我们RT-Thread Studio工程基础之上讲一讲RT-Thread内核启动流程.
    的头像 发表于 06-20 00:30 4422次阅读
    <b class='flag-5'>RT-Thread</b>记录(二、<b class='flag-5'>RT-Thread</b>内核<b class='flag-5'>启动</b><b class='flag-5'>流程</b>)

    从0到1教你写RT-Thread之新建工程

    仿真,然后我们再开始一步一步地教大家把 RT-Thread 内核从 0 到 1 写出来,让大家彻底搞懂 RT-Thread的内部实现和设计的哲学思想。最后我们再把 RT-Thread 移植到STM32
    发表于 11-18 14:17 1170次阅读

    RT-Thread的C语言编码规范

    这是一份 RT-Thread 开发人员的开发指引。RT-Thread 做为一份开源软件,它需要由不同的人采用合作的方式完成,这份文档是开发
    的头像 发表于 02-21 16:50 2080次阅读

    如何使用RT-Thread Studio创建支持HPM6750开发板RT-Thread项目

    StudioRT-Thread Studio安装程序下载页面RT-Thread Studio安装程序下载完成后,运行安装程序,一路下一步即可完成RT-Thread Studio的安装。添加HPM6750
    发表于 06-08 11:22

    RT-Thread编程指南

    RT-Thread编程指南——RT-Thread开发组(2015-03-31)。RT-Thread做为国内有较大影响力的开源实时操作系统,本文是RT
    发表于 11-26 16:06 0次下载

    RT-Thread开发,如何有效学习RT-Thread的五个步骤

    RT-Thread推出RT-Thread Inside战略开放RT-Thread开发平台授权合作,与硬件十万个为什么合作首次推出第一款RT-
    的头像 发表于 09-25 09:55 3.4w次阅读
    <b class='flag-5'>RT-Thread</b><b class='flag-5'>开发</b>,如何有效学习<b class='flag-5'>RT-Thread</b>的五个步骤

    RT-Thread开源作品秀】基于RT-Thread的星务平台研究

    本作品为了验证星务软件在RT-Thread系统运行的可行性,底层是否能够驱动星务软件,同时扩展RT-Thread应用范围。ART-Pi作为卫星下位机,...
    发表于 01-25 18:26 5次下载
    【<b class='flag-5'>RT-Thread</b>开源作品秀】基于<b class='flag-5'>RT-Thread</b>的星务平台研究

    如何使用xmake工具来编译rt-thread工程

     在最新的 rt-thread 主仓库,支持使用 xmake 工具来编译 rt-thread 工程。   
    的头像 发表于 05-11 15:03 1878次阅读
    如何使用xmake工具来编译<b class='flag-5'>rt-thread</b>工程

    RT-Thread学习笔记 RT-Thread的架构概述

    的种种优越之处。RT-Thread 是一款完全由国内团队开发维护的嵌入式实时操作系统(RTOS),具有完全的自主知识产权。经过 16 个年头的沉淀,伴随着物联网的兴起,它正演变成一个功能强大、组件丰富
    的头像 发表于 07-09 11:27 4002次阅读
    <b class='flag-5'>RT-Thread</b>学习笔记 <b class='flag-5'>RT-Thread</b>的架构概述

    RT-Thread文档_RT-Thread 简介

    RT-Thread文档_RT-Thread 简介
    发表于 02-22 18:22 5次下载
    <b class='flag-5'>RT-Thread</b>文档_<b class='flag-5'>RT-Thread</b> 简介

    RT-Thread文档_其他开发板 上手指南

    RT-Thread文档_其他开发板 上手指南
    发表于 02-22 18:27 0次下载
    <b class='flag-5'>RT-Thread</b>文档_其他<b class='flag-5'>开发板</b> 上手指南

    RT-Thread文档_RT-Thread SMP 介绍与移植

    RT-Thread文档_RT-Thread SMP 介绍与移植
    发表于 02-22 18:31 7次下载
    <b class='flag-5'>RT-Thread</b>文档_<b class='flag-5'>RT-Thread</b> SMP 介绍与移植

    基于rt-thread的socket通信设计

    最近再研究 rt-thread 的通信 ,想设计出 eps8266(多个) rt-thread(作为中控) 服务器的通信框架,使用的开发板是 潘多拉
    的头像 发表于 10-13 15:02 697次阅读
    基于<b class='flag-5'>rt-thread</b>的socket通信设计