新浪博客

PX4源代码分析,Nuttx系统启动(转)

2017-04-01 11:04阅读:
写在前面
最近弄PX4飞控,使用了Nuttx实时操作系统。想必跑裸机而言,使用了操作系统的复杂度是不一样的。至少来说在初期复杂度增加的可不止一点点。
然而在初期,最痛苦的还是Nuttx的资料不够,简直是稀少。所以也就只能一点点地啃源码以期望能够理清Nuttx的系统流程与基本轮廓。毕竟跑系统不比跑裸机,自己用到的资源一目了然。引入了C++之后有些东西就更加显得不是那么的清晰了。
其实到这个文档写完《1.2 命令》的时候我才发现我反了一个很严重的错误:我一直都在以裸奔的方式在分析PX4,这给我带来了极大的麻烦,使得我一开始就在代码中找寻应用启动的过程。其实Nuttx只是启动了系统而已,对于应用,那是在脚本中启动的。
1. 走流程
Nuttx系统我并没
有完全搞透,只是从系统的第一条代码到我们的飞控启动的基本轮廓理清了而已,各中细节还得深入分析。
那么我们先来讲解系统启动后的第一条代码即入口。
1.1 入口
PX4使用的是STM32,入口自然就在stm32_start.c中:
void __start(void)
{
const uint32_t *src;
uint32_t *dest;

#ifdef CONFIG_ARMV7M_STACKCHECK


__asm__ volatile ('sub r10, sp, %0' : : 'r'(CONFIG_IDLETHREAD_STACKSIZE - 64) : );
#endif



stm32_clockconfig();
stm32_fpuconfig();
stm32_lowsetup();
stm32_gpioinit();
showprogress('A');



for (dest = &_sbss; dest < &_ebss; )
{
*dest++ = 0;
}

showprogress('B');



for (src = &_eronly, dest = &_sdata; dest < &_edata; )
{
*dest++ = *src++;
}

showprogress('C');



#ifdef USE_EARLYSERIALINIT
up_earlyserialinit();
#endif
showprogress('D');



#ifdef CONFIG_NUTTX_KERNEL
stm32_userspace();
showprogress('E');
#endif



stm32_boardinitialize();
showprogress('F');



showprogress('\r');
showprogress('');
os_start();



for(;;);
}
我们可以先通过注释大概了解这里都做了哪些事情。在这之前还有一个程序在跑:bootloader。这个不是我们今天讨论的话题。在最后我们看到调用了一个os_start()函数也就是接下来系统将启动,对应的源文件是os_start.c:
void os_start(void)
{
int i;

slldbg('Entry');




dq_init(&g_readytorun);
dq_init(&g_pendingtasks);
dq_init(&g_waitingforsemaphore);
#ifndef CONFIG_DISABLE_SIGNALS
dq_init(&g_waitingforsignal);
#endif
#ifndef CONFIG_DISABLE_MQUEUE
dq_init(&g_waitingformqnotfull);
dq_init(&g_waitingformqnotempty);
#endif
#ifdef CONFIG_PAGING
dq_init(&g_waitingforfill);
#endif
dq_init(&g_inactivetasks);
sq_init(&g_delayed_kufree);
#if defined(CONFIG_NUTTX_KERNEL) && defined(CONFIG_MM_KERNEL_HEAP)
sq_init(&g_delayed_kfree);
#endif



g_lastpid = 0;
for (i = 0; i < CONFIG_MAX_TASKS; i++)
{
g_pidhash[i].tcb = NULL;
g_pidhash[i].pid = INVALID_PROCESS_ID;
}



g_pidhash[ PIDHASH(0)].tcb = &g_idletcb.cmn;
g_pidhash[ PIDHASH(0)].pid = 0;




bzero((void*)&g_idletcb, sizeof(struct task_tcb_s));
g_idletcb.cmn.task_state = TSTATE_TASK_RUNNING;
g_idletcb.cmn.entry.main = (main_t)os_start;



#if CONFIG_TASK_NAME_SIZE > 0
strncpy(g_idletcb.cmn.name, g_idlename, CONFIG_TASK_NAME_SIZE-1);
#endif



#if defined(CONFIG_CUSTOM_STACK) || !defined(CONFIG_NUTTX_KERNEL)
#if CONFIG_TASK_NAME_SIZE > 0
g_idletcb.argv[0] = g_idletcb.cmn.name;
#else
g_idletcb.argv[0] = (char*)g_idlename;
#endif
#endif



dq_addfirst((FAR dq_entry_t*)&g_idletcb, (FARdq_queue_t*)&g_readytorun);



up_initial_state(&g_idletcb.cmn);




#ifdef CONFIG_HAVE_WEAKFUNCTIONS
if (sem_initialize != NULL)
#endif
{
sem_initialize();
}



{
FAR void *heap_start;
size_t heap_size;



up_allocate_heap(&heap_start, &heap_size);
kumm_initialize(heap_start, heap_size);

#if defined(CONFIG_NUTTX_KERNEL) && defined(CONFIG_MM_KERNEL_HEAP)


up_allocate_kheap(&heap_start, &heap_size);
kmm_initialize(heap_start, heap_size);
#endif
}



#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS)
#ifdef CONFIG_HAVE_WEAKFUNCTIONS
if (task_initialize != NULL)
#endif
{
task_initialize();
}
#endif



#ifdef CONFIG_HAVE_WEAKFUNCTIONS
if (irq_initialize != NULL)
#endif
{
irq_initialize();
}



#ifdef CONFIG_HAVE_WEAKFUNCTIONS
if (wd_initialize != NULL)
#endif
{
wd_initialize();
}



#ifndef CONFIG_DISABLE_CLOCK
#ifdef CONFIG_HAVE_WEAKFUNCTIONS
if (clock_initialize != NULL)
#endif
{
clock_initialize();
}
#endif

#ifndef CONFIG_DISABLE_POSIX_TIMERS
#ifdef CONFIG_HAVE_WEAKFUNCTIONS
if (timer_initialize != NULL)
#endif
{
timer_initialize();
}
#endif



#ifndef CONFIG_DISABLE_SIGNALS
#ifdef CONFIG_HAVE_WEAKFUNCTIONS
if (sig_initialize != NULL)
#endif
{
sig_initialize();
}
#endif



#ifndef CONFIG_DISABLE_MQUEUE
#ifdef CONFIG_HAVE_WEAKFUNCTIONS
if (mq_initialize != NULL)
#endif
{
mq_initialize();
}
#endif



#ifndef CONFIG_DISABLE_PTHREAD
#ifdef CONFIG_HAVE_WEAKFUNCTIONS
if (pthread_initialize != NULL)
#endif
{
pthread_initialize();
}
#endif



#if CONFIG_NFILE_DESCRIPTORS > 0
#ifdef CONFIG_HAVE_WEAKFUNCTIONS
if (fs_initialize != NULL)
#endif
{
fs_initialize();
}
#endif



#ifdef CONFIG_NET
#if 0
if (net_initialize != NULL)
#endif
{
net_initialize();
}
#endif



up_initialize();



#ifdef CONFIG_HAVE_WEAKFUNCTIONS
if (lib_initialize != NULL)
#endif
{
lib_initialize();
}




#ifdef HAVE_TASK_GROUP
DEBUGVERIFY(group_allocate(&g_idletcb));
#endif



DEBUGVERIFY(group_setupidlefiles(&g_idletcb));



#ifdef HAVE_TASK_GROUP
DEBUGVERIFY(group_initialize(&g_idletcb));
g_idletcb.cmn.group->tg_flags = GROUP_FLAG_NOCLDWAIT;
#endif




DEBUGVERIFY(os_bringup());




sdbg('Beginning Idle Loop');
for (;;)
{


#ifndef CONFIG_SCHED_WORKQUEUE


if (kmm_trysemaphore() == 0)
{
sched_garbagecollection();
kmm_givesemaphore();
}
#endif



up_idle();
}
}
我们看到这里初始化了各种资源,最后进入了一个空闲任务。而在空闲任务的前面我们看到对其他任务也进行了初始化,这是在os_bringup.c文件中:
int os_bringup(void)
{
int taskid;



#if !defined(CONFIG_DISABLE_ENVIRON) && defined(CONFIG_PATH_INITIAL)
(void)setenv('PATH', CONFIG_PATH_INITIAL, 1);
#endif



#ifdef CONFIG_PAGING
svdbg('Starting paging thread');

g_pgworker = KERNEL_THREAD('pgfill', CONFIG_PAGING_DEFPRIO,
CONFIG_PAGING_STACKSIZE,
(main_t)pg_worker, (FAR char * const*)NULL);
DEBUGASSERT(g_pgworker > 0);
#endif



#ifdef CONFIG_SCHED_WORKQUEUE
#ifdef CONFIG_SCHED_HPWORK

#ifdef CONFIG_SCHED_LPWORK
svdbg('Starting high-priority kernel worker thread');
#else
svdbg('Starting kernel worker thread');
#endif

g_work[HPWORK].pid = KERNEL_THREAD(HPWORKNAME, CONFIG_SCHED_WORKPRIORITY,
CONFIG_SCHED_WORKSTACKSIZE,
(main_t)work_hpthread, (FAR char *const *)NULL);
DEBUGASSERT(g_work[HPWORK].pid > 0);



#ifdef CONFIG_SCHED_LPWORK

svdbg('Starting low-priority kernel worker thread');

g_work[LPWORK].pid = KERNEL_THREAD(LPWORKNAME, CONFIG_SCHED_LPWORKPRIORITY,
CONFIG_SCHED_LPWORKSTACKSIZE,
(main_t)work_lpthread, (FAR char *const *)NULL);
DEBUGASSERT(g_work[LPWORK].pid > 0);

#endif
#endif

#if defined(CONFIG_NUTTX_KERNEL) && defined(CONFIG_SCHED_USRWORK)


DEBUGASSERT(USERSPACE->work_usrstart != NULL);
taskid = USERSPACE->work_usrstart();
DEBUGASSERT(taskid > 0);
#endif

#endif



svdbg('Starting init thread');



#ifdef CONFIG_BOARD_INITIALIZE
board_initialize();
#endif



#ifdef CONFIG_NUTTX_KERNEL
DEBUGASSERT(USERSPACE->us_entrypoint != NULL);
taskid = TASK_CREATE('init', SCHED_PRIORITY_DEFAULT,
CONFIG_USERMAIN_STACKSIZE, USERSPACE->us_entrypoint,
(FAR char * const *)NULL);
#else
taskid = TASK_CREATE('init', SCHED_PRIORITY_DEFAULT,
CONFIG_USERMAIN_STACKSIZE,
(main_t)CONFIG_USER_ENTRYPOINT,
(FAR char * const *)NULL);
#endif
ASSERT(taskid > 0);



#if !defined(CONFIG_DISABLE_ENVIRON) && defined(CONFIG_PATH_INITIAL)
(void)clearenv();
#endif

return OK;
}
这里跟我们关系最大的就是init进程。在Linux中也有init进程,是所有应用程序进程的祖先进程,进程ID为1。这里其实类似。Init进程启动了所有应用程序进程。不过这里需要注意的是CONFIG_USER_ENTRYPOINT是在配置文件中指定的,相当于一个宏:
CONFIG_USER_ENTRYPOINT='nsh_main'
int nsh_main(int argc, char *argv[])
{
int exitval = 0;
int ret;



#if defined(CONFIG_HAVE_CXX) && defined(CONFIG_HAVE_CXXINITIALIZE)
up_cxxinitialize();
#endif



#if defined(CONFIG_LIBC_EXECFUNCS) && defined(CONFIG_EXECFUNCS_SYMTAB)
exec_setsymtab(CONFIG_EXECFUNCS_SYMTAB, 0);
#endif



#if defined(CONFIG_FS_BINFS) && (CONFIG_BUILTIN)
ret = builtin_initialize();
if (ret < 0)
{
fprintf(stderr, 'ERROR: builtin_initialize failed: %d', ret);
exitval = 1;
}
#endif



nsh_initialize();



#ifdef CONFIG_NSH_TELNET
ret = nsh_telnetstart();
if (ret < 0)
{


fprintf(stderr, 'ERROR: Failed to start TELNET daemon: %d', ret);
exitval = 1;
}
#endif



#ifdef CONFIG_NSH_CONSOLE
ret = nsh_consolemain(0, NULL);



fprintf(stderr, 'ERROR: nsh_consolemain() returned: %d', ret);
exitval = 1;
#endif

return exitval;
}
这里就相当于Linux中启动了shell,然后去执行初始化脚本。
int nsh_consolemain(int argc, char *argv[])
{
FAR struct console_stdio_s *pstate = nsh_newconsole();
int ret;

DEBUGASSERT(pstate);



#ifdef CONFIG_NSH_ROMFSETC
(void)nsh_initscript(&pstate->cn_vtbl);
#endif



#ifdef CONFIG_NSH_USBDEV_TRACE
usbtrace_enable(TRACE_BITSET);
#endif



ret = nsh_session(pstate);



nsh_exit(&pstate->cn_vtbl, ret);
return ret;
}
在这里实际上nsh_initscript和nsh_session都会去执行命令,但nsh_session从代码上看更像是Linux的命令行参数解析, nsh_initscript回去执行/etc/init.d/rcS脚本。
int nsh_initscript(FAR struct nsh_vtbl_s *vtbl)
{
static bool initialized;
bool already;
int ret = OK;



sched_lock();
already = initialized;
initialized = true;
sched_unlock();



if (!already)
{
ret = nsh_script(vtbl, 'init', NSH_INITPATH);
}

return ret;
}
这里我们需要对NSH_INITPATH进行展开:
# ifndef CONFIG_NSH_ROMFSMOUNTPT
# define CONFIG_NSH_ROMFSMOUNTPT '/etc'
# endif

# ifndef CONFIG_NSH_INITSCRIPT
# define CONFIG_NSH_INITSCRIPT 'init.d/rcS'
# endif

# undef NSH_INITPATH
# define NSH_INITPATH CONFIG_NSH_ROMFSMOUNTPT '/'CONFIG_NSH_INITSCRIPT
所以到了这里我们需要把目光转向启动脚本。后面我们所需要的很多东西,包括PX4飞控主线程都是在脚本中意命令的方式启动的。所以说如果抛开脚本,单纯看源码是看不出PX4飞控主线程是如何启动的,因为这些个东西根本就不在源码中。
1.2 命令
说到脚本,我们当然知道脚本中最多的是命令。在Nuttx中命令跟Linux还是有很大的区别的。
看到这里,可能有的人会想Nuttx到底跟Linux有什么关系。其实Nuttx只是跟Linux某些地方相似而已。而我多次提到Linux只是在分析Nuttx的时候以Linux为坐标,仅此而已。有了坐标,我就不至于完全迷失了方向。
通过前面的分析,我们很容易想到接下来的代码分析将从nsh_script开始。
int nsh_script(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd,
FAR const char *path)
{
char *fullpath;
FILE *stream;
char *buffer;
char *pret;
int ret = ERROR;



fullpath = nsh_getfullpath(vtbl, path);
if (!fullpath)
{
return ERROR;
}



buffer = nsh_linebuffer(vtbl);
if (buffer)
{


stream = fopen(fullpath, 'r');
if (!stream)
{
nsh_output(vtbl, g_fmtcmdfailed, cmd, 'fopen', NSH_ERRNO);
nsh_freefullpath(fullpath);
return ERROR;
}



do
{


fflush(stdout);
pret = fgets(buffer, CONFIG_NSH_LINELEN, stream);
if (pret)
{


ret = nsh_parse(vtbl, buffer);
}
}
while (pret && ret == OK);
fclose(stream);
}

nsh_freefullpath(fullpath);
return ret;
}
这段代码并不难理解,就是打开了我们的脚本文件,然后逐行读取并解析执行。那显然nsh_parse就是解析这些脚本的关键。
int nsh_parse1(FAR struct nsh_vtbl_s *vtbl, char *cmdline)
{


#if defined(CONFIG_NSH_BUILTIN_APPS) && (!defined(CONFIG_NSH_FILE_APPS) || !defined(CONFIG_FS_BINFS))
ret = nsh_builtin(vtbl, argv[0], argv, redirfile, oflags);
#endif
{


ret = nsh_execute(vtbl, argc, argv);
}
return nsh_saveresult(vtbl, true);
}
需要说明的是这个函数是删减过的,就是说只留下了我最想要看到的部分。nsh_builtin和nsh_execute分别对应两个数组:g_builtins和g_cmdmap。什么区别呢?在我看来这两组命令是可以合并到一起的,但如果真的这样做了会有一个问题,用户扩展不方便。于是对其进行区分。像ls这样的命令不需要用户实现及处理,就作为系统命令在g_cmdmap数组中,而作为用户的应用程序如ArduPilot完全有用户决定,便作为用户命令在g_builtins数组中。对于系统命令我们常用的基本都已经实现,通过特定的宏进行选择编译。而用户命令我们只需按照相应规则编写一个入口函数然并设定堆栈大小然后放到g_builtins数组中即可。
Nuttx本身提供了一个g_builtins数组,但PX4没有使用。PX4使用了一些技巧,利用Makefile在编译的时候自动生成g_builtins数组。这样做好处是我们修改源码的时候可以不关心这个数组,但坏处也显而易见:增加了我们刚接触是阅读源码的壁垒。
那么关于命令部分我们先讲解到这里。下面我们将稍微看下脚本。
1.3 启动脚本
前面我们说过Nuttx的启动脚本是/etc/init.d/rcS,跟Linux是一样的。这样做也有一个好处,就是如果你对Linux比较熟悉即便你完全不看源码只要看下根文件系统你就能够找到启动脚本并进行分析。
启动脚本并不少,我们不逐条分析。
set MODE autostart
set USB autoconnect
if rgbled start
then
set HAVE_RGBLED 1
rgbled rgb 16 16 16
else
set HAVE_RGBLED 0
fi
echo '[init] looking for microSD...'
if mount -t vfat /dev/mmcsd0 /fs/microsd
then
echo '[init] card mounted at /fs/microsd'
set HAVE_MICROSD 1
tone_alarm start
else
echo '[init] no microSD card found'
set HAVE_MICROSD 0
tone_alarm 2
if [ $HAVE_RGBLED == 1 ]
then
rgbled rgb 16 0 0
fi
fi
if [ -f /fs/microsd/etc/rc ]
then
echo '[init] reading /fs/microsd/etc/rc'
sh /fs/microsd/etc/rc
fi
if [ -f /fs/microsd/etc/rc.txt ]
then
echo '[init] reading /fs/microsd/etc/rc.txt'
sh /fs/microsd/etc/rc.txt
fi
if [ $USB != autoconnect ]
then
echo '[init] not connecting USB'
else
if sercon
then
echo '[init] USB interface connected'
else
echo '[init] No USB connected'
fi
fi
if [ -f /etc/init.d/rc.APM -a $HAVE_MICROSD == 1 -a ! -f /fs/microsd/APM/nostart]
then
echo Running rc.APM
sh /etc/init.d/rc.APM
else
nshterm /dev/ttyACM0 &
fi
我们看到先是设置了环境变量,然后打开了RGB灯,就是PX4飞控上的那个三色高亮LED,设置了这几个等如何闪烁。然后挂载SD卡并执行SD卡中的脚本,接下来自动连接USB,最后是执行rc.APM脚本。所以PX4要用的东西基本上都在rc.APM脚本中启动。
set deviceA /dev/ttyACM0
if [ -f /fs/microsd/APM ]
then
echo 'APM file found - renaming'
mv /fs/microsd/APM /fs/microsd/APM.old
fi
if [ -f /fs/microsd/APM/nostart ]
then
echo 'APM/nostart found - skipping APM startup'
sh /etc/init.d/rc.error
fi
if [ -f /bin/reboot ]
then
echo 'binfs already mounted'
else
echo 'Mounting binfs'
if mount -t binfs /dev/null /bin
then
echo 'binfs mounted OK'
else
sh /etc/init.d/rc.error
fi
fi
set sketch NONE
if rm /fs/microsd/APM/boot.log
then
echo 'removed old boot.log'
fi
set logfile /fs/microsd/APM/BOOT.LOG
if [ ! -f /bin/ArduPilot ]
then
echo '/bin/ardupilot not found'
sh /etc/init.d/rc.error
fi
if mkdir /fs/microsd/APM > /dev/null
then
echo 'Created APM directory'
fi
if [ -f /bin/lsm303d ]
then
echo 'Detected FMUv2 board'
set BOARD FMUv2
else
echo 'Detected FMUv1 board'
set BOARD FMUv1
fi
if [ $BOARD == FMUv1 ]
then
set deviceC /dev/ttyS2
if [ -f /fs/microsd/APM/AUXPWM.en ]
then
set deviceD /dev/null
else
set deviceD /dev/ttyS1
fi
else
set deviceC /dev/ttyS1
set deviceD /dev/ttyS2
fi
if uorb start
then
echo 'uorb started OK'
else
sh /etc/init.d/rc.error
fi
if [ -f /fs/microsd/APM/mkblctrl ]
then
echo 'Setting up mkblctrl driver'
echo 'Setting up mkblctrl driver' >> $logfile
mkblctrl -d /dev/pwm_output
fi
if [ -f /fs/microsd/APM/mkblctrl_+ ]
then
echo 'Setting up mkblctrl driver +'
echo 'Setting up mkblctrl driver +' >> $logfile
mkblctrl -mkmode + -d /dev/pwm_output
fi
if [ -f /fs/microsd/APM/mkblctrl_x ]
then
echo 'Setting up mkblctrl driver x'
echo 'Setting up mkblctrl driver x' >> $logfile
mkblctrl -mkmode x -d /dev/pwm_output
fi
set HAVE_PX4IO false
if px4io start norc
then
set HAVE_PX4IO true
else
echo Loading /etc/px4io/px4io.bin
tone_alarm MBABGP
if px4io update /etc/px4io/px4io.bin
then
echo 'upgraded PX4IO firmware OK'
tone_alarm MSPAA
else
echo 'Failed to upgrade PX4IO firmware'
tone_alarm MNGGG
fi
sleep 1
if px4io start norc
then
set HAVE_PX4IO true
tone_alarm start
fi
fi
if [ $HAVE_PX4IO == true ]
then
echo 'PX4IO board OK'
if px4io checkcrc /etc/px4io/px4io.bin
then
echo 'PX4IO CRC OK'
else
echo 'PX4IO CRC failure'
echo 'PX4IO CRC failure' >> $logfile
tone_alarm MBABGP
if px4io forceupdate 14662 /etc/px4io/px4io.bin
then
sleep 1
if px4io start norc
then
echo 'PX4IO restart OK'
echo 'PX4IO restart OK' >> $logfile
tone_alarm MSPAA
else
echo 'PX4IO restart failed'
echo 'PX4IO restart failed' >> $logfile
tone_alarm MNGGG
sh /etc/init.d/rc.error
fi
else
echo 'PX4IO update failed'
echo 'PX4IO update failed' >> $logfile
tone_alarm MNGGG
fi
fi
else
echo 'No PX4IO board found'
echo 'No PX4IO board found' >> $logfile
if [ $BOARD == FMUv2 ]
then
sh /etc/init.d/rc.error
fi
fi
if [ $BOARD == FMUv1 -a $deviceD == /dev/ttyS1 ]
then
echo 'Setting FMU mode_serial'
fmu mode_serial
else
echo 'Setting FMU mode_pwm'
fmu mode_pwm
fi
echo 'Starting APM sensors'
if ms5611 start
then
echo 'ms5611 started OK'
else
sh /etc/init.d/rc.error
fi
if adc start
then
echo 'adc started OK'
else
sh /etc/init.d/rc.error
fi
if [ $BOARD == FMUv1 ]
then
echo 'Starting FMUv1 sensors'
if hmc5883 start
then
echo 'hmc5883 started OK'
if hmc5883 calibrate
then
echo 'hmc5883 calibrate OK'
else
echo 'hmc5883 calibrate failed'
echo 'hmc5883 calibrate failed' >> $logfile
tone_alarm MSBBB
fi
else
echo 'hmc5883 start failed'
echo 'hmc5883 start failed' >> $logfile
sh /etc/init.d/rc.error
fi
if mpu6000 start
then
echo 'mpu6000 started OK'
else
sh /etc/init.d/rc.error
fi
if l3gd20 start
then
echo 'l3gd20 started OK'
else
echo 'No l3gd20'
echo 'No l3gd20' >> $logfile
fi
else
echo 'Starting FMUv2 sensors'
if hmc5883 -C -X start
then
echo 'Have external hmc5883'
else
echo 'No external hmc5883'
fi
if hmc5883 -C -I -R 4 start
then
echo 'Have internal hmc5883'
else
echo 'No internal hmc5883'
fi
if mpu6000 -X -R 4 start
then
echo 'Found MPU6000 external'
set HAVE_FMUV3 true
else
echo 'No MPU6000 external'
set HAVE_FMUV3 false
fi
if [ $HAVE_FMUV3 == true ]
then
if mpu6000 -R 14 start
then
echo 'Found MPU6000 internal'
else
echo 'No MPU6000'
echo 'No MPU6000' >> $logfile
sh /etc/init.d/rc.error
fi
if l3gd20 -X -R 4 start
then
echo 'l3gd20 external started OK'
else
echo 'No l3gd20'
sh /etc/init.d/rc.error
fi
if lsm303d -X -R 6 start
then
echo 'lsm303d external started OK'
else
echo 'No lsm303d'
sh /etc/init.d/rc.error
fi
else
if mpu6000 start
then
echo 'Found MPU6000'
else
echo 'No MPU6000'
echo 'No MPU6000' >> $logfile
fi
if l3gd20 start
then
echo 'l3gd20 started OK'
else
sh /etc/init.d/rc.error
fi
if lsm303d start
then
echo 'lsm303d started OK'
else
sh /etc/init.d/rc.error
fi
fi
fi
if ets_airspeed start
then
echo 'Found ETS airspeed sensor'
fi
if meas_airspeed start
then
echo 'Found MEAS airspeed sensor'
fi
if ll40ls start
then
echo 'Found ll40ls sensor'
fi
if mb12xx start
then
echo 'Found mb12xx sensor'
fi
echo 'Trying PX4IO board'
if mtd start /fs/mtd
then
echo 'started mtd driver OK'
else
echo 'failed to start mtd driver'
echo 'failed to start mtd driver' >> $logfile
sh /etc/init.d/rc.error
fi
if mtd readtest /fs/mtd
then
echo 'mtd readtest OK'
else
echo 'failed to read mtd'
echo 'failed to read mtd' >> $logfile
sh /etc/init.d/rc.error
fi
if [ $BOARD == FMUv2 ]
then
if mtd rwtest /fs/mtd
then
echo 'mtd rwtest OK'
else
echo 'failed to test mtd'
echo 'failed to test mtd' >> $logfile
sh /etc/init.d/rc.error
fi
fi
echo Starting ArduPilot $deviceA $deviceC $deviceD
if ArduPilot -d $deviceA -d2 $deviceC -d3 $deviceD start
then
echo ArduPilot started OK
else
sh /etc/init.d/rc.error
fi
echo 'rc.APM finished'
这个脚本比较长。第一句应该是对设备进行重定向,然后对上一次的日志进行备份并创建日志。然后通过lsm303d判断PX4的版本,是V1还是V2。换句话说V1跟V2最大的区别是V2有lsm303d传感器,这是一个地磁传感器,而且内置加计。从这里我们也可以看出相比V1,V2在设计上采用了内置地磁的设计。然后根据硬件版本分别初始化输入输出设备。
后面那个uorb没搞懂是什么。后面的mkblctrl感觉上像是机型配置相关的,具体是与不是还有待证实。然后是跟新IO板的固件。接下来启动的fmu想必是一个很重要的进程。然后又是根据硬件版本进行初始化,启动传感器。后面那几个估计也是传感器,具体还需要查。Mtd呢肯定是跟存储设备相关的。
最后,ArduPilot便是对于我们来讲最重要的一个进程,这个进程启动基本上就表示我们的飞控可以正常使用了。

我的更多文章

下载客户端阅读体验更佳

APP专享