RTOS有俩种主流的启动方式:
1.将准备工作(包括创建线程)都做好后,启动RTOS调度器
2.先创建一个启动线程并启动RTOS调度器,在启动线程中创建其他应用线程
俩种方式有着截然不同的思路,下面简单介绍一下俩种启动方式的流程:
在main函数中完成以下任务:
通过伪代码来简单描述一下启动流程:
int main( void )
{
/* 硬件初始化 */
HardWare_Init();
/* RTOS系统初始化 */
RTOS_Init();
/* 创建应用线程 */
RTOS_ThreadCreate( Thread1 );
RTOS_ThreadCreate( Thread2 );
……
/* 启动RTOS调度器 */
RTOS_Start();
}
启动步骤如下:
通过伪代码来简单描述一下启动流程:
int main( void )
{
/* 初始化硬件 */
HardWare_Init();
/* 初始化RTOS系统 */
RTOS_Init();
/* 创建启动线程 */
RTOS_ThreadCreate( AppThreadStart );
/* 启动RTOS调度器 */
RTOS_Start();
}
void AppThreadStart( void *arg )
{
/* 创建应用线程 */
RTOS_ThreadCreate( Thread1 );
RTOS_ThreadCreate( Thread2 );
……
/* 关闭启动线程 */
RTOSThreadClose( AppThreadStart );
}
注意:在起始线程中创建完应用线程后要,关闭起始线程。
ucos第一种第二种启动方法都可以;freertos和RTT默认使用第二种。
RTT中使用的是第二种启动方式,也就是先创建一个启动线程并开启系统调度,然后在启动线程中创建其他应用线程。上面说了,一些初始化工作和创建启动线程的工作都在main
函数中完成,可实际中RTT例程中main函数中并没有做这些工作!!!
RTT的实际启动流程比上面讲的高级一点,大体流程如下:
__main
函数__main
函数中初始化系统堆栈__main
函数后跳转到$Sub$$main
函数$Sub$$main
函数中按照上面方式二进行配置$Super$$main
函数跳转到main
函数中main
函数中创建应用线程所以RTT的启动流程相比于之前的,只是把方式二的操作放在$Sub$$main
函数中执行了,下面捋一捋RTT的具体启动流程:
复位函数是由汇编语言写的,代码如下:
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
CPSID I ; 关中断
LDR R0, =0xE000ED08
LDR R1, =__Vectors
STR R1, [R0]
LDR R2, [R1]
MSR MSP, R2
LDR R0, =SystemInit
BLX R0
CPSIE i ; 开中断
LDR R0, =__main ;在这里进入__main函数
BX R0
ENDP
关于汇编语言之前整理过一部分,链接:汇编语言基本知识
其余的代码和RTT启动流程没啥关系。
__main
函数主要就是初始化一下系统堆栈,然后在函数的最后跳转到$Sub$$main
函数,由于__main
函数中的其他部分与RTT启动流程没关系,所以就不讲了,__main
函数主要就是一个过渡的作用,从复位函数过渡到$Sub$$main
函数。
$Sub$$
函数与$Super$$
函数是编译器(此处是KEIL)自带的一对扩展函数,它们的作用就是对要扩展的函数执行预操作
,比如$Sub$$main
就是在main
函数执行前,执行的函数,执行完$Sub$$main
函数后,用$Super$$main
函数跳转到main
函数中。
了解$Sub$$main
函数的原理后,来看一下RTT中$Sub$$main
函数的代码:
$Sub$$main
函数int $Sub$$main( void )
{
/* 关闭中断 */
rt_hw_interrupt_disable();
/* 启动RTT */
rtthread_startup();
return 0;
}
$Sub$$main
函数中是通过rtthread_startup()
函数来启动RTT的。
rtthread_startup()
函数int rtthread_startup( void )
{
/* 关中断,在硬件初始化前习惯性的关中断 */
rt_hw_interrupt_disable();
/* 硬件初始化 */
rt_hw_board_init();
/* 定时器初始化 */
rt_system_timer_init();
/* 调度器初始化 */
rt_system_scheduler_init();
#ifdef RT_USING_SIGNALS
/* 信号量初始化 */
rt_system_signal_init();
#endif
/* 创建启动线程 */
rt_application_init();
/* 初始化定时器线程 */
rt_system_timer_thread_init();
/* 初始化空闲线程 */
rt_thread_idle_init();
/* 启动调度器 */
rt_system_scheduler_start();
return 0;
}
重点是rt_application_init()
函数,因为创建启动线程就在此函数中进行,要注意的是rt_application_init()
函数只是对启动线程进行创建,并没有启动系统调度,系统调度是在最后才启动的。
rt_application_init()
函数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_THREAD_PRIORITY_MAX / 3,
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_THREAD_PRIORITY_MAX / 3,
20);
RT_ASSERT(result == RT_EOK);
/* if not define RT_USING_HEAP, using to eliminate the warning */
(void)result;
#endif
rt_thread_startup(tid);
}
创建启动线程有静态创建和动态创建俩种方法,具体的方式可以根据宏定义来选择。
可以看到启动线程的人口函数是main_thread_entry()
,下面再看一下main_thread_entry()
函数。
main_thread_entry
函数void main_thread_entry( void *parameter )
{
extern int main( void );
extern int $Super$$main( void );
/* RTT组件初始化 */
rt_components_init();
/* 跳转到main函数 */
$Super$$main();
}
int main(void)
{
/*
* 开发板硬件初始化, RT-Thread 系统初始化已经在 main 函数之前完成,
* 即在 component.c 文件中的 rtthread_startup()函数中完成了。 (1)
* 所以在 main 函数中,只需要创建线程和启动线程即可。
*/
thread1 = /* 线程控制块指针 */
rt_thread_create("thread1", /* 线程名字,字符串形式 */
thread1_entry, /* 线程入口函数 */
RT_NULL, /* 线程入口函数参数 */
HREAD1_STACK_SIZE, /* 线程栈大小,单位为字节 */
THREAD1_PRIORITY, /* 线程优先级,数值越大,优先级越小 */
THREAD1_TIMESLICE); /* 线程时间片 */
if (thread1 != RT_NULL)
rt_thread_startup(thread1);
else
return -1;
thread2 = /* 线程控制块指针 */
rt_thread_create("thread2", /* 线程名字,字符串形式 */
thread2_entry, /* 线程入口函数 */
RT_NULL, /* 线程入口函数参数 */
THREAD2_STACK_SIZE, /* 线程栈大小,单位为字节 */
THREAD2_PRIORITY, /* 线程优先级,数值越大,优先级越小 */
THREAD2_TIMESLICE); /* 线程时间片 */
if (thread2 != RT_NULL)
rt_thread_startup(thread2);
else
return -1;
(4)
thread3 = /* 线程控制块指针 */
rt_thread_create("thread3", /* 线程名字,字符串形式 */
thread3_entry, /* 线程入口函数 */
RT_NULL, /* 线程入口函数参数 */
THREAD3_STACK_SIZE, /* 线程栈大小,单位为字节 */
THREAD3_PRIORITY, /* 线程优先级,数值越大,优先级越小 */
THREAD3_TIMESLICE); /* 线程时间片 */
if (thread3 != RT_NULL)
rt_thread_startup(thread3);
else
return -1;
/* 执行到最后,通过 LR 寄存器执行的地址返回 */ (5)
}
在main函数中只需要创建相应的应用线程即可!