Nuttx系统启动流程
借用一位大神的图[天又亮了的博客http://blog.csdn.net/zhumaill/article/details/23261543]
__start-- #处理器执行的第一条指令
|
v
stm32_clockconfig()------ #初始化时钟
|
v
rcc_reset() #复位rcc
stm32_stdclockconfig() #初始化标准时钟
rcc_enableperipherals() #使能外设时钟
|
--------------------
|
v
stm32_fpuconfig() #配置fpu,shenzhou/nsh未调用
stm32_lowsetup() #基本初始化串口,之后可以使用up_lowputc()
stm32_gpioinit() #初始化gpio,只是调用stm32_gpioremap()设置重映射
up_earlyserialinit() #初始化串口,之后可以使用up_putc()
stm32_boardinitialize()-- #板级初始化
|
v
stm32_spiinitialize() #初始化spi,只是调用stm32_configgpio()设置gpio
stm32_usbinitialize() #初始化usb,只是调用stm32_configgpio()设置gpio
board_led_initialize() #初始化led,只是调用stm32_configgpio()设置gpio
|
--------------------
|
v
os_start()--------------- #初始化操作系统
|
v
dq_init() #初始化各种状态的任务列表(置为null)
g_pidhash[i]= #初始化唯一可以确定的元素--进程ID
g_pidhash[PIDHASH(0)]= #分配空闲任务的进程ID为0
g_idletcb= #初始化空闲任务的任务控制块
sem_initialize()-- #初始化信号量
|
v
dq_init() #将信号量队列置为null
sem_initholders() #初始化持有者结构以支持优先级继承,shenzhou/nsh未调用
|
--------
|
v
up_allocate_heap() #分配用户模式的堆(设置堆的起点和大小)
kumm_initialize() #初始化用户模式的堆
up_allocate_kheap() #分配内核模式的堆,shenzhou/nsh未调用
kmm_initialize() #初始化内核模式的堆,shenzhou/nsh未调用
task_initialize() #初始化任务数据结构,shenzhou/nsh未调用
irq_initialize() #将所有中断向量都指向同一个异常中断处理程序
wd_initialize() #初始化看门狗数据结构
clock_initialize() #初始化rtc
timer_initialize() #配置POSIX定时器
sig_initialize() #初始化信号
mq_initialize() #初始化命名消息队列
pthread_initialize() #初始化线程特定的数据,空函数
fs_initialize()--- #初始化文件系统
|
v
sem_init() #初始化节点信号量为1
files_initialize() #初始化文件数组,空函数
|
--------
|
v
net_initialize()-- #初始化网络
|
v
uip_initialize() #初始化uIP层
net_initroute() #初始化路由表,shenzhou/nsh未调用
netdev_seminit() #初始化网络设备信号量
arptimer_init() #初始化ARP定时器
|
--------
|
v
up_initialize()--- #处理器特定的初始化
|
v
up_calibratedelay()#校准定时器
up_addregion() #增加额外的内存段
up_irqinitialize() #设置中断优先级,关联硬件异常处理函数
up_pminitialize() #初始化电源管理,shenzhou/nsh未调用
up_dmainitialize() #初始化DMA,shenzhou/nsh未调用
up_timerinit() #初始化定时器中断
devnull_register() #注册/dev/null
devzero_register() #注册/dev/zero,shenzhou/nsh未调用
up_serialinit() #注册串口控制台/dev/console和串口/dev/ttyS0
up_rnginitialize() #初始化并注册随机数生成器,shenzhou/nsh未调用
up_netinitialize() #初始化网络,是arch/arm/src/chip/stm32_eth.c中的
up_usbinitialize() #初始化usb驱动,shenzhou/nsh未调用
board_led_on() #打开中断使能led,但很快会被其它地方的led操作改变状态
|
--------
|
v
lib_initialize() #初始化c库,空函数
group_allocate() #分配空闲组
group_setupidlefiles() #在空闲任务上创建stdout、stderr、stdin
group_initialize() #完全初始化空闲组
os_bringup()------ #创建初始任务
|
v
KEKERNEL_THREAD() #启动内核工作者线程
board_initialize() #最后一刻的板级初始化,shenzhou/nsh未调用
TASK_CREATE() #启动默认应用程序
|
--------
|
v
for up_idle() #空闲任务循环
|
--------------------
|
v
for(;;) #不应该到达这里
下面我就以FRDM-k64为主介绍一下nuttx的启动代码流程图,因为使用的是Nuttx7.18的代码可能和上面大神的有点区别。
_start()函数启动开始(在Kinetis_start.c)
kinetis_wddisable();//关闭看门狗
kinetis_clockconfig();//配置时钟
stm32_fpuconfig();//初始化FPU功能
kinetis_lowsetup();//芯片最初的一些初始化
#ifdef USE_EARLYSERIALINIT
up_earlyserialinit();//系统早期的串口初始化
#endif
#ifdef CONFIG_BUILD_PROTECTED
kinetis_userspace();//初始化用户空间
#endif
kinetis_boardinitialize();//初始化板上的所有资源
/* Then start NuttX */
os_start();//系统开始运行
/* Shouldn't get here */
for (; ; );//不会运行到这
os_start()函数解析,在Os_start.c文件中
/*初始化RTOS数据,初始化任务链表*/
dq_init(&g_readytorun);
dq_init(&g_pendingtasks);
dq_init(&g_waitingforsemaphore);
//这里还初始化了系统运行的其他一些初始参数
/* Initialize the IDLE task TCB 初始化任务块*******************************************/
//在这是初始化一些信号量,内存分配等等系统运行必要参数,上面大神写的比较清楚,我主要看了下面两个函数
up_initialize();//特定处理器的初始化
os_bringup();//创建初始任务
up_initialize(void);在nuttx/arch/arm/src/common/Up_initialize.c中
/* Calibrate the timing loop */
up_calibratedelay();//校正时钟
/* Colorize the interrupt stack */
up_color_intstack();//初始化中断任务块
/* Add any extra memory fragments to the memory manager */
up_addregion();
/* Initialize the interrupt subsystem */
up_irqinitialize();//初始化中断表
/*
在这还有其他的一些初始化比如usb初始化、telnet初始化等等
*/
//最后就是
board_autoled_on(LED_IRQSENABLED);//初始化板载中断,有中断产生就点亮RGB
os_bringup();在nuttx/sched/init/Os_bringup.c中
/* Start the page fill worker kernel thread that will resolve page faults.
* This should always be the first thread started because it may have to
* resolve page faults in other threads
*/
os_pgworker();
/* Start the worker thread that will serve as the device driver "bottom-
* half" and will perform misc garbage clean-up.
*/
os_workqueues();
/* Once the operating system has been initialized, the system must be
* started by spawning the user initialization thread of execution. This
* will be the first user-mode thread.
*/
os_start_application();//开始应用程序初始化
os_start_application();在nuttx/sched/init/Os_bringup.c中
static inline void os_start_application(void)//inline 定义的是一个内联函数
{
#ifdef CONFIG_BOARD_INITTHREAD//如果定义了,机会创建一个线程来执行os_do_appstart();如果没有就是直接调用os_do_appstart();
int pid;
/* Do the board/application initialization on a separate thread of
* execution.
*/
pid = kernel_thread("AppBringUp", CONFIG_BOARD_INITTHREAD_PRIORITY,
CONFIG_BOARD_INITTHREAD_STACKSIZE,
(main_t)os_start_task, (FAR char * const *)NULL);
ASSERT(pid > 0);
#else
/* Do the board/application initialization on this thread of execution. */
os_do_appstart();
#endif
}
os_do_appstart();在nuttx/sched/init/Os_bringup.c中
static inline void os_do_appstart(void)
{
int pid;
#ifdef CONFIG_BOARD_INITIALIZE
/* Perform any last-minute, board-specific initialization, if so
* configured.
*/
board_initialize();//板级初始化
#endif
/* Start the application initialization task. In a flat build, this is
* entrypoint is given by the definitions, CONFIG_USER_ENTRYPOINT. In
* the protected build, however, we must get the address of the
* entrypoint from the header at the beginning of the user-space blob.
*/
sinfo("Starting init thread\n");
#ifdef CONFIG_BUILD_PROTECTED
DEBUGASSERT(USERSPACE->us_entrypoint != NULL);
pid = task_create("init", SCHED_PRIORITY_DEFAULT,
CONFIG_USERMAIN_STACKSIZE, USERSPACE->us_entrypoint,
(FAR char * const *)NULL);//创建任务,
#else
pid = task_create("init", SCHED_PRIORITY_DEFAULT,
CONFIG_USERMAIN_STACKSIZE,
(main_t)CONFIG_USER_ENTRYPOINT,
(FAR char * const *)NULL);//创建任务,CONFIG_USER_ENTRYPOINT指的是NSH_MAIN(nsh_main)
#endif
ASSERT(pid > 0);
}
最后执行up_idle()这个空闲任务,到这整个Nuttx启动就结束了。启动完后有一个可以通过串口进行调试的串口,类似ssh这种命令窗口,但是不知道为什么,只有dev和proc两个文件夹,难道是K64没有完整的移植好,还是我的使用有误。
在整个分析过程中有一部分还不是很清楚,比如任务调度器的运行原理,还是缺乏操作系统原理的知识。
希望正在学习的同学可以一起交流一些,同学习PX4才学习这个系统,如果有什么错误欢迎大家更正。同时也希望大神级的前辈指导一下。