关于NuttX这个系统需要后续继续研究,这里我解读一下从系统启动到各个任务模块运行完毕后的这个过程,暂时没有关于带入操作系统的概念,这段时间在进行NuttX的学习,系统的资料实在太少啦,过段时间再聊NuttX。

首先观察.map文件,确定入口

为什么我选择观察这个文件呢,因为之前在单片机上玩儿freertos的时候经常拿这个看编译后固件占用资源,知道它里面会有编译的时候的链接关系,我希望在这里找到程序的入口,PX4的固件用的Nuttx而且编译器是gcc-arm-none-eabi,我也没找到类似于startup.s这种的启动文件,启动规则应该是nuttx自己规定的。
我运行的编译命令是

make px4_fmu-v3_default

编译PX4完成后目录在/build/px4_fmu-v3_default/
分别对应
/build/px4_fmu-v3_default/px4_fmu-v3_default.map
/build/px4_fmu-v3_default/external/Build/px4io_firmware/px4_io-v2_default.map
这两个对应的其中一个是飞控fmu处理器的固件,一个是飞控io处理器的固件。


可以看到这里面第一个就是我要的

NuttX/nuttx/arch/arm/src/libarch.a(stm32_start.o)
                              (__start)

一个叫stm32_start的源文件里的__start函数,都叫这个名字了,那肯定就是入口啦。
于是我全局搜索了一下它,
/boards/px4/fmu-v3/nuttx-config/scripts/script.ld
/boards/px4/io-v2/nuttx-config/scripts/script.ld

ENTRY(__start)        /* treat __start as the anchor for dead code stripping */

这两个作为链接脚本,直接说明了入口函数。

__start

对应路径/platforms/nuttx/NuttX/nuttx/arch/arm/src/stm32/stm32_start.c
我觉得这个跟NuttX有关,NuttX有一些不同的芯片对应相同的上层文件,比如这个stm32_start.c,FMU芯片和IO芯片对应的是这同一个文件。
void __start(void)描述:This is the reset entry point.就是复位进入的函数,跟32的启动文件进入main函数一个意思
一开始进行了一系列初始化一系列初始化
stm32_clockconfig();
stm32_fpuconfig();
stm32_lowsetup();
stm32_gpioinit();
showprogress(‘A’);
跟踪下去函数,会发现会根据不同的宏定义去配置不同的东西,也就是说不同的芯片对应宏定义不同
以stm32_gpioinit为例子,它进入stm32_gpioremap函数
关于stm32_gpioremap函数的说明是
Based on configuration within the .config file, this function will remaps positions of alternative functions.
里面有一些宏定义CONFIG_STM32_STM32F10XX来自于nuttx/config.h
config.h说自己是自动生成的,它是根据.config生成的


这里说一下这个.config文件怎么来的
如果之前做过linux驱动移植,那么这东西一定比较眼熟了,他是通过剪裁得来的,NuttX的所有操作都对标的LINUX规范


目前先不纠结这个,知道它是根据不同的.config生成不同的宏定义,在同样的上层文件里配置不同的初始化工作就行,这些初始化的宏定义和初始化函数不会去人为的去改动,统一由nuttx的剪裁来完成,并且自动生成。

继续看void __start(void)
最终由go_nx_start进入nx_start()
函数定义在/platforms/nuttx/NuttX/nuttx/sched/init/nx_start.c
整个系统的启动在这里,启动了内存分配,时间调度,调试中端和log等等
感兴趣区域在最下方
/_ Then start the other CPUs _/
DEBUGVERIFY(nx_smp_start());

/_ Create initial tasks and bring-up the system _/
DEBUGVERIFY(nx_bringup());
nx_smp_start()
启动了所有cpu,PIXHAWK包含一个F4和一个F1,启动操作最终指向了nuttx底层
最终进入
/_ Create initial tasks and bring-up the system _/
DEBUGVERIFY(nx_bringup());
nx_bringup()
在这个里面进入了 nx_create_initthread();
然后进入nx_start_application();
执行
nx_start_application();

  pid = nxtask_create("init", CONFIG_USERMAIN_PRIORITY,
                      CONFIG_USERMAIN_STACKSIZE,
                      (main_t)CONFIG_USER_ENTRYPOINT,
                      (FAR char * const *)NULL);

进入CONFIG_USER_ENTRYPOINT
这个CONFIG_USER_ENTRYPOINT开始就是我们最关注的应用层启动啦
对于FMU芯片,是nsh_main
对于IO芯片,是user_start
user_start是直接进入了一个死循环for (;;) {
nsh_main则是进入了进入/platforms/nuttx/NuttX/apps/system/nsh/nsh_main.c中的main函数,然后进入nsh_task()

nsh_task()

在这个里面进入
nsh_consolemain
对应/platforms/nuttx/NuttX/apps/nshlib/nsh_consolemain.c
执行(void)nsh_initscript(&pstate->cn_vtbl);
跳到ret = nsh_script(vtbl, “init”, NSH_INITPATH);
NSH_INITPATH就是执行脚本的指向了

这里是怎么实现运行这个脚本的我不去研究,属于nuttx自己的东西。
可以看到最终指向init.d/rcS
这些启动脚本大都放在一个目录下面

编译后都存放在
/build/px4_fmu-v3_default/genromfs/init.d/rc.airship_apps

init.d/rcS

观察rcS文件

/ROMFS/px4fmu_common/init.d/rcS
最开始是一些变量的设置
uorb首先被打开,其实就是定义了一些函数在里面
SD卡清空和挂载
然后就是autostart,有一个判断

if [ -f $FRC ]
then
    sh $FRC
else

$FRC指向/fs/microsd/etc/rc.txt,在QGC终端找不到这个,说明会进行后面的操作
然后加载参数mtd_params,这个是可以在nsh终端里面看到,但是查看不了内容
而后蜂鸣器响了
执行脚步/etc/init.d/rc.board_defaults
对应文件/boards/px4/fmu-v3/init/rc.board_defaults,用来更新bootloader的,一般我看也用不上
然后在 if [ -f $IOFW ]后判断是否进行对io芯片的固件更新
rc_update start RC update (map raw RC input to calibrate manual control),就是说得到原始值为校准做准备
而后有一个判断要不要Enable HITL/SIH mode on next boot的参数SYS_HITL,默认是0
默认运行rc.board_sensors,/boards/px4/fmu-v3/init/rc.board_sensors


rc.board_sensors

打开rgbled、adc、外置I2C(hmc5883)、内置SPI(默认检测ms5611 or ms5607)
下面有一个判断if ver hwtypecmp V30
ver是一个代码不是命令/src/systemcmds/ver/ver.c
我在终端里面看到我的版本是V2M
然后启动icm20608g -s -b 1 -R 14 start
mpu6000 -s -b 1 -R 10 start/mpu9250 -s -b 1 -R 10 start
接下来启动

# l3gd20 (external/isolated SPI4)  l3gd20 -s -b 4 -R 4 start
# lsm303d (external/isolated SPI4) lsm303d -s -b 4 -R 6 start
# ms5611 (external/isolated SPI4)  ms5611 -s -b 4 start

注意SPI4是外接的传感器,板子上是没有的
其实这些有的可以删除掉
暂时不讨论这个


回到rcS
sh /etc/init.d/rc.sensors
对应/ROMFS/px4fmu_common/init.d/rc.sensors
是一些通用的外接传感器的初始化
而后是电源管理启动和commander启动
再后面就是一些专门的外设的初始化
我直接跳到了sh $BOARD_RC_MAVLINK(/etc/init.d/rc.board_mavlink)
对应文件/boards/px4/fmu-v3/init/rc.board_mavlink

# Start MAVLink on the USB port
mavlink start -d /dev/ttyACM0

然后
sh /etc/init.d/rc.serial
Note: rc.serial is auto-generated from Tools/serial/generate_config.py
说是自动生成的,嗯,看不太懂,大概是进行串口设备的挂载之类的。


继续往下,是
sh /etc/init.d/rc.vehicle_setup
rc.vehicle_setup is the entry point for rc.interface,rc.fw_apps, rc.mc_apps, rc.rover_apps, and rc.vtol_apps.
根据不同的机型对应不同脚本
默然不打开任何东西ekf2 start
常用的四轴启动了下面俩
sh /etc/init.d/rc.interface
sh /etc/init.d/rc.mc_apps
对应在/ROMFS/px4fmu_common/init.d/
在rc.interface里面会启动rc.io进行px4io start,进行了一些初始化
在rc.mc_apps里面会启动姿态估计和位置估计(attitude_estimator_q+local_position_estimator或者EKF2)
mc_rate_control start
还有如下控制应用

#
# Start Multicopter Attitude Controller.
#
mc_att_control start
#
# Start Multicopter Position Controller.
#
mc_hover_thrust_estimator start
mc_pos_control start
#
# Start Multicopter Land Detector.
#
land_detector start multicopter

回到rcS,接着navigator start开启导航模块
然后是光流模块、黑山羊这种外部扩展的启动
最后启动日志sh /etc/init.d/rc.logging

嗯,over

启动过程其实最重要的就是启动脚本的顺序了。
后面改什么东西基本上都需要去改哪些脚本。
看了好长时间。。。