在了解px4启动之前我们需要了解一下bootloader。Bootloader是在操作系统内核运行之前运行,可以初始化硬件设备,建立内存空间映射图等,整个系统的加载启动任务就是完全由Bootloader来完成的。它还是嵌入式系统在加电后执行的第一段代码,在它完成cpu和相关硬件的初始化后,再将操作系统映像或固化的嵌入式应用程序装在内存中然后跳转到操作系统所在的空间,启动操作系统运行(后面会有一篇专门学习px4的Bootloader)
现在我们来看一下32是如何启动的:
代码位置:Firmware/build_px4fmu-v2_default/px4fmu-v2/Nuttx/nuttx/arch/arm/src/stm32/stm32_start.c
__start-- #处理器执行的第一条指令(px4使用的是stm32,入口在stm32_start.c中)
|
v
stm32_clockconfig()------ #初始化时钟
|
v
rcc_reset() #复位rcc
stm32_stdclockconfig() #初始化标准时钟
rcc_enableperipherals() #使能外设时钟
------------------------------------------------------------------
stm32_fpuconfig() #配置fpu
|
v
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
up_ledinit(); #初始化led,只是调用stm32_configgpio()设置gpio
|
----------------------------------------------------------------------------------------------
在stm32_start.c文件中我们会看到这么一句话:
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() #初始化持有者结构以支持优先级继承
|
--------
|
v
up_allocate_heap() #分配用户模式的堆(设置堆的起点和大小)
kumm_initialize() #初始化用户模式的堆
up_allocate_kheap() #分配内核模式的堆
kmm_initialize() #初始化内核模式的堆
task_initialize() #初始化任务数据结构
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() #初始化路由表
netdev_seminit() #初始化网络设备信号量
arptimer_init() #初始化ARP定时器
|
--------
|
v
up_initialize()--- #处理器特定的初始化
|
v
up_calibratedelay() #校准定时器
up_addregion() #增加额外的内存段
up_irqinitialize() #设置中断优先级,关联硬件异常处理函数
up_pminitialize() #初始化电源管理
up_dmainitialize() #初始化DMA
up_timerinit() #初始化定时器中断
devnull_register() #注册/dev/null
devzero_register() #注册/dev/zero
up_serialinit() #注册串口控制台/dev/console和串口/dev/ttyS0
up_rnginitialize() #初始化并注册随机数生成器
up_netinitialize() #初始化网络,是arch/arm/src/chip/stm32_eth.c中的
up_usbinitialize() #初始化usb驱动
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() #最后一刻的板级初始化
TASK_CREATE() #启动默认应用程序
|
--------
|
v
forup_idle() #空闲任务循环
|
--------------------
|
v
for(;;) #不应该到达这里
我们看到这里初始化了各种资源,最后进入一个空闲任务,而在空闲任务的前面我们看到对其他任务也进行了初始化。这是在os_bringup.c文件中。
这里是启动默认的应用程序,入口点由CONFIG_USER_ENTRYPOINT给出。
这里就相当于Linux中启动了shell,然后去执行初始化脚本。
进入控制台程序,也就是nsh_consolemain()函数。在这里实际上nsh_initscript和nsh_session都会去执行命令,但是前者是执行启动脚本也就是rcS,后者是执行用户程序。
到了这里我就发现px4飞控主线程都是在脚本中以命令的方式启动的,抛开脚本,单纯看源码是看不出px4飞控主线程是如何启动的。